/* label.c
********************************************************************************
* label.c  -- Routines for PDS label objects
*
* User routines:
*       Pro_OpenLabel()		creates a PDS label object.
*       Pro_LabelCount()	returns the number of tables/columns/items.
*       Pro_LabelName()		returns the name of a table/column.
*       Pro_LabelFind()		returns the index of a named table/column.
*	Pro_LabelSampling()	returns the sampling parameters of a column.
*       Pro_LabelInt()		returns the integer value of a label keyword.
*       Pro_LabelFloat()	returns the floating point value of a keyword.
*       Pro_LabelString()	returns the string value of a keyword.
*
* Programmer routines:
*	XPro_LabelInfo()	returns the key parameters of a column.
*
* Neil Heather & Mark Showalter, PDS Ring-Moon Systems Node, March 1998.
*******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "profile.h"
#include "fortran.h"

#include "oal.h"

/*************************
 * Data type definitions *
 *************************/

typedef struct ZPRO_COLUMN_STRUCT {
    ODLTREE	odltree;
    RL_CHAR	name[PRO_NAMELEN+1];
    RL_INT4     items;		/* Default: 1          */
    RL_FLT8     missing;	/* Default: -HUGE_VAL  */
    RL_FLT8     invalid;	/* Default: -HUGE_VAL  */
    RL_FLT8     offset;		/* Default: 0.0        */
    RL_FLT8     scale;		/* Default: 1.0        */
} ZPRO_COLUMN;

typedef struct ZPRO_TABLE_STRUCT {
    ODLTREE	odltree;
    RL_CHAR	name[PRO_NAMELEN+1];
    RL_CHAR	xname[PRO_NAMELEN+1];
    RL_INT4	nrows, ncolumns;
    RL_FLT8	x1, x2, dx;
    ZPRO_COLUMN	*columns;
} ZPRO_TABLE;

typedef struct ZPRO_LABEL_STRUCT {
    XPRO_CLASS	class;
    ODLTREE	odltree;  /* the root node of the label file */
    RL_CHAR	filename[PRO_FILELEN+1];
    RL_INT4	ntables;
    ZPRO_TABLE	*tables;
} ZPRO_LABEL;

/********************************
 * Internal function prototypes *
 ********************************/

static void          ZPro_FreeLabel  RL_PROTO((RL_VOID *pointer));
static void          ZPro_PrintLabel RL_PROTO((RL_VOID *pointer));
static ZPRO_LABEL   *ZPro_LabelPtr   RL_PROTO((PRO_OBJECT *object));
static ZPRO_TABLE   *ZPro_TablePtr   RL_PROTO((PRO_OBJECT *object,
                                               RL_INT4 ntable));
static ZPRO_COLUMN  *ZPro_ColumnPtr  RL_PROTO((PRO_OBJECT *object,
                                               RL_INT4 ntable,
                                               RL_INT4 ncolumn));

static RL_BOOL ZPro_GetParam  RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_CHAR **ptr));
static RL_BOOL ZPro_GetInt    RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_INT4 *value));
static RL_BOOL ZPro_GetFloat  RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_FLT8 *value));
static RL_BOOL ZPro_GetString RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_CHAR *value, RL_INT4 maxlen));
static void    ZPro_DefInt    RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_INT4 dfault, RL_INT4 *value));
static void    ZPro_DefFloat  RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_FLT8 dfault, RL_FLT8 *value));
static void    ZPro_DefFloat2 RL_PROTO((ODLTREE odltree, RL_CHAR *name1,
                                        RL_CHAR *name2,
                                        RL_FLT8 dfault, RL_FLT8 *value));
static void    ZPro_DefString RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_CHAR *dfault,
                                        RL_CHAR *value, RL_INT4 maxlen));
static void    ZPro_DefName   RL_PROTO((ODLTREE odltree, RL_CHAR *name,
                                        RL_CHAR *unit, RL_CHAR *dfault,
                                        RL_CHAR *value, RL_INT4 maxlen));

/********************
 * Static variables *
 ********************/

static XPRO_CLASS label_class = {XPRO_LABEL_CLASS, "PDS label", NULL};

#define TEMP_MAXLEN 255
static RL_CHAR    temp_name[TEMP_MAXLEN+1];

/*********************
 * Macro definitions *
 *********************/

#define SUPPRESS_MESSAGES 1

/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
*	Pro_OpenLabel (label.c)
*$ Abstract:
*	This routine opens the specified PDS label file and creates a label
*	object.
*$ Keywords:
*	PROFILE
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	PRO_OBJECT	*Pro_OpenLabel(labelfile)
*	RL_CHAR		*labelfile;
*$ Inputs:
*      *labelfile	name of PDS label file.
*$ Outputs:
*	none
*$ Returns:
*	pointer to a new label object.
*$ Detailed_description:
*	This routine opens the specified PDS label file and creates a label
*	object.  It parses the label to find the TABLE and SERIES objects.  Use
*	Pro_ColumnSeries() (see column.c) to create a series object from one of
*	the columns.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	Memory is allocated.  The label file is opened.
*$ Examples:
*	This snippet of code creates a label object for a PDS file called
*	"table.tab":
*
*	PRO_OBJECT	*label;
*
*	label = Pro_OpenLabel("table.tab");
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_SETUP_FAILURE	if the label cannot be read or parsed or has no
*				TABLE or SERIES objects.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

PRO_OBJECT	*Pro_OpenLabel(labelfile)
RL_CHAR		*labelfile;
{
ODLTREE		root_node, node;
PRO_OBJECT	*new;
ZPRO_LABEL	*label;
ZPRO_TABLE	*table;
ZPRO_COLUMN	*column;
RL_INT4         i, j, ntables, nseries;

    /****************************
     * Read and parse the label *
     ****************************/

    root_node = OaParseLabelFile(labelfile, NULL, ODL_EXPAND_STRUCTURE,
			(unsigned short) SUPPRESS_MESSAGES);
    if (root_node != NULL) root_node = OaConvertLabel(root_node);

    if (root_node == NULL || (oa_errno >= 500 && oa_errno < 900)) {
	(void) sprintf(xpro_message, "label parsing failure\n\
Unable to read/parse label in file \"%s\"\n\
OA Library error code %d",
	labelfile, oa_errno);
	RL_RaiseError("PRO_SETUP_FAILURE", xpro_message);

	if (root_node != NULL) OdlFreeTree(root_node);
	return NULL;
    }

    /**********************************
     * Count the SERIES & TABLE nodes *
     **********************************/

    for (i=1; ; i++) {
	node = OdlFindObjDesc(root_node, "*SERIES", NULL, NULL,
			(unsigned long) i, ODL_RECURSIVE_DOWN);
	if (node == NULL) break;
    }
    ntables = i - 1;

    for (i=1; ; i++) {
	node = OdlFindObjDesc(root_node, "*TABLE", NULL, NULL,
			(unsigned long) i, ODL_RECURSIVE_DOWN);
	if (node == NULL) break;
    }
    ntables += i - 1;

    if (ntables == 0) {
	(void) sprintf(xpro_message, "label setup failure\n\
No TABLE or SERIES objects found in label \"%s\"",
	labelfile);
	RL_RaiseError("PRO_SETUP_FAILURE", xpro_message);

	OdlFreeTree(root_node);
	return NULL;
    }

    /***************************************
     * Initialize the ZPRO_LABEL structure *
     ***************************************/

    label = (ZPRO_LABEL *) XRL_Malloc(sizeof(ZPRO_LABEL));
    if (label == NULL) return NULL;

    label->class = label_class;
    label->odltree = root_node;

    (void) strncpy(label->filename, labelfile, PRO_FILELEN);
    label->filename[PRO_FILELEN] = '\0';

    label->ntables = ntables;

    label->tables = (struct ZPRO_TABLE_STRUCT *)
	XRL_Malloc(ntables * sizeof(ZPRO_TABLE));
    if (label->tables == NULL) {
	ZPro_FreeLabel((RL_VOID *) label);
	return NULL;
    }

    /*****************************************
     * Fill in the SERIES and TABLE pointers *
     *****************************************/

    for (i=1; ; i++) {
	node = OdlFindObjDesc(root_node, "*SERIES", NULL, NULL,
			(unsigned long) i, ODL_RECURSIVE_DOWN);
	if (node == NULL) break;

	label->tables[i-1].odltree = node;
    }
    nseries = i - 1;

    for (i=1; ; i++) {
	node = OdlFindObjDesc(root_node, "*TABLE", NULL, NULL,
			(unsigned long) i, ODL_RECURSIVE_DOWN);
	if (node == NULL) break;

	label->tables[nseries + i-1].odltree = node;
    }

    /*****************************
     * Fill each table structure *
     *****************************/

    for (i=0; i < ntables; i++) {
	table = &(label->tables[i]);
	node = table->odltree;

	ZPro_DefString(node, "NAME", "", table->name, PRO_NAMELEN);

	ZPro_DefInt(node, "ROWS",    1, &(table->nrows));
	ZPro_DefInt(node, "COLUMNS", 1, &(table->ncolumns));

	ZPro_DefFloat2(node, "MINIMUM_SAMPLING_PARAMETER",
	                     "START_SAMPLING_PARAMETER",
	                     1., &(table->x1));
	ZPro_DefFloat2(node, "MAXIMUM_SAMPLING_PARAMETER",
	                     "STOP_SAMPLING_PARAMETER",
	                     (RL_FLT8) table->nrows, &(table->x2));
	ZPro_DefFloat(node,  "SAMPLING_PARAMETER_INTERVAL",
	                     1., &(table->dx));
	ZPro_DefName(node,   "SAMPLING_PARAMETER_NAME",
	                     "SAMPLING_PARAMETER_UNIT",
	                     "ROW_INDEX", table->xname, PRO_NAMELEN);

	/******************************
	 * Allocate column structures *
	 ******************************/

	table->columns = (struct ZPRO_COLUMN_STRUCT *)
	    XRL_Malloc(table->ncolumns * sizeof(ZPRO_COLUMN));
	if (table->columns == NULL) {
		ZPro_FreeLabel((RL_VOID *) label);
		return NULL;
	}

	/**********************************************
	 * For each table, fill each column structure *
	 **********************************************/

	for (j=1; j <= table->ncolumns; j++) {
	    node = OdlFindObjDesc(table->odltree, "*COLUMN", NULL, NULL,
			(unsigned long) j, ODL_RECURSIVE_DOWN);
                                         /* ODL_THIS_OBJECT doesn't work! */

	    if (node == NULL) {		/* This should never happen */
		label->tables[i].ncolumns = j-1;
		break;
	    }

	    column = &(table->columns[j-1]);
	    column->odltree = node;

	    (void) sprintf(xpro_message, "COLUMN_%d", j);
	    ZPro_DefName(node, "NAME", "UNIT", xpro_message,
			column->name, PRO_NAMELEN);

	    ZPro_DefInt(node, "ITEMS", 1, &(column->items));
	    ZPro_DefFloat2(node, "MISSING_CONSTANT", "MISSING", -HUGE_VAL,
			&(column->missing));
	    ZPro_DefFloat2(node, "INVALID_CONSTANT", "INVALID", -HUGE_VAL,
			&(column->invalid));
	    ZPro_DefFloat(node, "OFFSET", 0., &(column->offset));
	    ZPro_DefFloat(node, "SCALING_FACTOR", 1., &(column->scale));
	}
    }

    /*********************
     * Create new object *
     *********************/

    new = XPro_MakeObject(0., 0., ZPro_FreeLabel, ZPro_PrintLabel,
                          (RL_VOID *) label);

    return new;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelCount (label.c)
*$ Abstract:
*	This routine returns the number of tables in a PDS label object, the
*	number of columns in a table, or the number of items in a column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_INT4		Pro_LabelCount(object, ntable, ncolumn)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable, ncolumn;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable		index of the selected table (0 or 1...n).
*	ncolumn		index of the selected column (0 or 1...n).
*$ Outputs:
*	none
*$ Returns:
*	If ntable is zero, this is the number of tables in the label;
*	If ncolumn is zero, this is the number of columns in the selected table;
*	Otherwise, this is the number of items in the selected column.
*$ Detailed_description:
*	This routine returns the number of tables in a PDS label object, the
*	number of columns in a table, or the number of items in a column.
*
*	The quantity returned depends on the which input parameters are set to
*	zero.  If ntable is zero, the total number of PDS TABLE and SERIES
*	objects is returned.  Otherwise, ntable is the index (starting with 1)
*	of the selected table.  If ncolumn is zero, the number of PDS COLUMN
*	objects in the selected table is returned.  Otherwise, ncolumn is the
*	index (starting with 1) of the selected column and the number of ITEMs
*	in the column is returned.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^TIME_SERIES	= "FOOBAR.TAB"
*	^DATA_TABLE	= "FOOBAR.DAT"
*	^IMAGE		= "FOOBAR.IMG"
*	OBJECT		= TIME_SERIES
*	  ... (3 columns defined, each with 1 item)
*	END_OBJECT	= TIME_SERIES
*	OBJECT		= DATA_TABLE
*	  ... (1 column defined, with 600 items)
*	END_OBJECT	= DATA_TABLE
*	OBJECT		= IMAGE
*	  ...
*	END_OBJECT	= IMAGE
*	END
*
*	Then Pro_LabelCount(label, 0, 0) returns 2 (number of tables or series);
*	     Pro_LabelCount(label, 1, 0) returns 3 (columns in TIME_SERIES);
*	     Pro_LabelCount(label, 1, 1) returns 1 (items in column);
*	     Pro_LabelCount(label, 2, 1) returns 600 (items in column);
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table or column index is out of range.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4		Pro_LabelCount(object, ntable, ncolumn)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn;
{
ZPRO_LABEL	*label;
ZPRO_TABLE	*table;
ZPRO_COLUMN	*column;

    /**********************
     * Return table count *
     **********************/

    if (ntable == 0) {
	label = ZPro_LabelPtr(object);
	if (label == NULL) return 0;

	return label->ntables;
    }

    /*********************************
     * Otherwise return column count *
     *********************************/

    if (ncolumn == 0) {
	table = ZPro_TablePtr(object, ntable);
	if (table == NULL) return 0;

	return table->ncolumns;
    }

    /*******************************
     * Otherwise return item count *
     *******************************/

    column = ZPro_ColumnPtr(object, ntable, ncolumn);
    if (column == NULL) return 0;

    return column->items;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelName (label.c)
*$ Abstract:
*	This routine returns the name of the specified table or column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_CHAR		*Pro_LabelName(object, ntable, ncolumn)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable, ncolumn;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*	ncolumn		index of the selected column (0 or 1...n).
*$ Outputs:
*	none
*$ Returns:
*	a pointer to a (read-only) string containing the desired name.
*	If ncolumn is zero, this is the name of the selected table; otherwise,
*	this is the name of the selected column.
*$ Detailed_description:
*	This routine returns the name of the specified table or column.
*
*	The quantity returned depends on the which input parameters are set to
*	zero.  If ncolumn is zero, the name of the PDS TABLE or SERIES object is
*	returned; otherwise, ncolumn is the index (starting with 1) of the
*	selected column and the routine returns the column's name.
*
*	COLUMN names are defined by the PDS label's NAME field, followed by a
*	space and (if present) the PDS label's UNIT field in parentheses.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  NAME				= OCCULTATION_GEOMETRY
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    NAME			= RECORD_INDEX
*	    UNIT			= 'N/A'
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    NAME			= INTERCEPT_RADIUS
*	    UNIT			= "km"
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then Pro_LabelName(label, 1, 0) returns "OCCULTATION_GEOMETRY";
*	     Pro_LabelName(label, 1, 1) returns "RECORD_INDEX";
*	     Pro_LabelName(label, 1, 2) returns "INTERCEPT_RADIUS (km)"
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table or column index is out of range.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_CHAR		*Pro_LabelName(object, ntable, ncolumn)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn;
{
ZPRO_TABLE	*table;
ZPRO_COLUMN	*column;

    /*********************
     * Return table name *
     *********************/

    if (ncolumn == 0) {
	table = ZPro_TablePtr(object, ntable);
	if (table == NULL) return NULL;

	return table->name;
    }

    /********************************
     * Otherwise return column name *
     ********************************/

    column = ZPro_ColumnPtr(object, ntable, ncolumn);
    if (column == NULL) return NULL;

    return column->name;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelXName (label.c)
*$ Abstract:
*	This routine returns the name of a table's X-coordinate.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_CHAR		*Pro_LabelXName(object, ntable)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*$ Outputs:
*	none
*$ Returns:
*	a pointer to a (read-only) string containing the desired name, or NULL
*	on a non-fatal error.
*$ Detailed_description:
*	This routine returns the name of a table's X-coordinate.  Names are
*	defined by the PDS label's SAMPLING_PARAMETER_NAME field, followed by a
*	space and (if present) the PDS label's SAMPLING_PARAMETER_UNIT field in
*	parentheses.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  NAME				= OCCULTATION_GEOMETRY
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    NAME			= RECORD_INDEX
*	    UNIT			= 'N/A'
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    NAME			= INTERCEPT_RADIUS
*	    UNIT			= "km"
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then Pro_LabelXName(label, 1) returns "RECORD_INDEX"
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table index is out of range.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_CHAR		*Pro_LabelXName(object, ntable)
PRO_OBJECT	*object;
RL_INT4		ntable;
{
ZPRO_TABLE	*table;
ZPRO_COLUMN	*column;

    table = ZPro_TablePtr(object, ntable);
    if (table == NULL) return NULL;

    return table->xname;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelFind (label.c)
*$ Abstract:
*	This routine returns the index of the named table or column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_INT4		Pro_LabelFind(object, ntable, name)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable;
*	RL_CHAR		*name;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*	name		name of the table or column to find.  For columns, this
*			string can optionally exclude units inside parentheses.
*$ Outputs:
*	none
*$ Returns:
*	index (1...n) of the selected table or column; 0 if not found.
*	If ntable is zero, this is the index of the table with the given name;
*	otherwise, this is the index of the column with the given name.
*$ Detailed_description:
*	This routine returns the index of the named table or column.
*
*	The quantity returned depends on the which input parameters are set to
*	zero.  If ntable is zero, the index (starting with 1) of the table with
*	the given name is returned.  Otherwise, ntable is the index of the
*	selected table and the number returned is the index of the column with
*	the given name.  For columns, if the name string does not contain units
*	(inside parentheses), then any units in the column name are ignored.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  NAME				= OCCULTATION_GEOMETRY
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    NAME			= RECORD_INDEX
*	    UNIT			= 'N/A'
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    NAME			= INTERCEPT_RADIUS
*	    UNIT			= "km"
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then Pro_LabelFind(label, 0, "OCCULTATION_GEOMETRY")  returns 1;
*	     Pro_LabelFind(label, 1, "RECORD_INDEX")          returns 1;
*	     Pro_LabelFind(label, 1, "INTERCEPT_RADIUS (km)") returns 2;
*	     Pro_LabelFind(label, 1, "INTERCEPT_RADIUS")      returns 2;
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table index is out of range.
*	PRO_NOT_FOUND		if no table or column of the given name exists.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4		Pro_LabelFind(object, ntable, name)
PRO_OBJECT	*object;
RL_INT4		ntable;
RL_CHAR		*name;
{
RL_INT4		ntables, ncolumns, lname, i;
RL_BOOL		hasunits;
RL_CHAR		*comp;
ZPRO_LABEL	*label;

    /**********************************
     * Search for matching table name *
     **********************************/

    if (ntable == 0) {
	ntables = Pro_LabelCount(object, 0, 0);
	for (i=1; i<=ntables; i++) {
	    if (strcmp(name, Pro_LabelName(object,i,0)) == 0) return i;
	}

	/* If no match is found, report error */
	label = ZPro_LabelPtr(object);
	(void) sprintf(xpro_message, "named table not found\n\
label contains no table named \"%s\"\n\
label file = \"%s\"",
				name, label->filename);

	RL_RaiseError("PRO_NOT_FOUND", xpro_message);
	return 0;
    }

    /*********************************************
     * Otherwise search for matching column name *
     *********************************************/

    /* Check for units in match string */
    hasunits = (strchr(name, '(') != NULL);
    lname = strlen(name);

    /* Search for matching table name */
    ncolumns = Pro_LabelCount(object, ntable, 0);
    for (i=1; i<=ncolumns; i++) {
	comp = Pro_LabelName(object,ntable,i);

	/* If match string has units, check for exact match */
	if (hasunits) {
	    if (strcmp(name, comp) == 0) return i;
	}

	/* Otherwise, match up to end, paren, or blank+paren */
	else {
	    if (strncmp(name, comp, lname) == 0) {
		if (comp[lname] == '\0' || comp[lname]   == '(') return i;
		if (comp[lname] == ' '  && comp[lname+1] == '(') return i;
	    }
	}
    }

    /* If no match is found, report error */
    label = ZPro_LabelPtr(object);
    (void) sprintf(xpro_message, "named column not found\n\
table #%d contains no column named \"%s\"\n\
label file = \"%s\"",
				ntable, name, label->filename);

    RL_RaiseError("PRO_NOT_FOUND", xpro_message);
    return 0;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelSampling (label.c)
*$ Abstract:
*	This routine returns the sampling parameters used by a specified table
*	or column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_INT4		Pro_LabelSampling(object, ntable, ncolumn, nitem,
*					x1, x2, dx)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable, ncolumn, nitem;
*	RL_FLT8		*x1, *x2, *dx;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*	ncolumn		index of the selected column (1...n) or 0 for row
*			sampling.
*	nitem		index of selected item, or 0 for all.
*$ Outputs:
*	*x1		first sampling parameter; not returned if x1==NULL.
*	*x2		last sampling parameter;  not returned if x2==NULL.
*	*dx		interval between samples; not returned if dx==NULL.
*$ Returns:
*	the number of samples in selected column/item pair; 0 on error.
*$ Detailed_description:
*	This routine returns the sampling parameters used by a specified table
*	or column.
*
*	If nitem is nonzero, then it is assumed that the only a single item per
*	row is to be used; in this case the sampling parameters for the column
*	match those for the row.  However, if nitem is zero in a column that
*	contains multiple items, then the items are treated as subsamples, so
*	the sample interval is correspondingly reduced.
*
*	Note that the row sampling parameters are returned if ncolumn is zero.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    ITEMS			= 1
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    ITEMS			= 100
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then Pro_LabelSampling(label, 1, 1, 1, &x1, &x2, &dx)
*			sets x1=-16., x2=1296., dx=1., returns 1313;
*	     Pro_LabelSampling(label, 1, 1, 0, &x1, &x2, &dx)
*			sets x1=-16., x2=1296., dx=1., returns 1313;
*	     Pro_LabelSampling(label, 1, 2, 1, &x1, &x2, &dx)
*			sets x1=-16., x2=1296., dx=1., returns 1313;
*	     Pro_LabelSampling(label, 1, 2, 0, &x1, &x2, &dx)
*			sets x1=-16., x2=1296.99, dx=0.01, returns 131300
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if table, column or item index is out of range.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4		Pro_LabelSampling(object, ntable, ncolumn, nitem, x1, x2, dx)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn, nitem;
RL_FLT8		*x1, *x2, *dx;
{
ZPRO_TABLE	*table;
ZPRO_COLUMN	*column;
RL_FLT8		scaled_dx;
RL_INT4		nrows;

    /**************
     * Find table *
     **************/

    table = ZPro_TablePtr(object, ntable);
    if (table == NULL) return 0;

    scaled_dx = table->dx;
    nrows = table->nrows;

    /*********************************************
     * Find column and optional expansion factor *
     *********************************************/

    if (ncolumn != 0) {
	column = ZPro_ColumnPtr(object, ntable, ncolumn);
	if (column == NULL) return 0;

	if (nitem < 0 || nitem > column->items)
	    XPro_IDomainError("item index", object, 1, column->items, nitem);

	if (nitem == 0) {
	    scaled_dx /= column->items;
	    nrows *= column->items;
	}
    }

    /*****************
     * Return values *
     *****************/

    if (x1 != NULL) *x1 = table->x1;
    if (x2 != NULL) *x2 = table->x2 + (table->dx - scaled_dx);
    if (dx != NULL) *dx = scaled_dx;

    return nrows;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelInt (label.c)
*$ Abstract:
*	This routine returns the integer value of a specified keyword in a PDS
*	label.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_INT4		Pro_LabelInt(object, ntable, ncolumn, keyword, dfault,
*					raise_error)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable, ncolumn, dfault;
*	RL_CHAR		*keyword;
*	RL_BOOL		raise_error;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable		table index, or 0 to search among top keywords only.
*	ncolumn		column index, or 0 to search among table keywords only.
*	keyword		keyword name.
*	dfault		default value to return on error.
*	raise_error	TRUE to raise PRO_EVALUATION_FAILURE on error;
*			FALSE otherwise.
*$ Outputs:
*	none
*$ Returns:
*	integer value of the keyword; default value on non-fatal error.
*$ Detailed_description:
*	This routine returns the integer value of a specified keyword in a PDS
*	label.
*
*	What section of the label is searched depends on the values of the
*	ntable and ncolumn parameters.  If ntable is zero, then only the
*	keywords at the top of the PDS label (above any PDS OBJECTs) are
*	searched.  If ntable is nonzero but ncolumn is zero, then the keywords
*	inside the TABLE/SERIES object but above the COLUMN objects are
*	searched, followed by the keywords at the top of the label.  If both
*	ntable and nseries are nonzero, then the keywords in the selected
*	COLUMN object are searched, followed by the keywords in the selected
*	TABLE/SERIES object, followed by the keywords at the top of the label.
*
*	If the keyword is not found in the PDS label, or is found but is not a
*	valid integer, the default value is returned.  In this case, error code
*	PRO_EVALUATION_FAILURE if the raise_error parameter is TRUE; otherwise
*	no error condition is raised.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	FOO		= 1313
*	ABC		= "DEF"
*	OBJECT		= SERIES
*	  FOO		= 1000
*	  BAR		= 999
*	  OBJECT	= COLUMN
*	    FOO		= 3
*	  END_OBJECT	= COLUMN
*	END_OBJECT	= SERIES
*	END
*
*	Then Pro_LabelInt(label, 1, 1, "FOO", -99, TRUE) returns 3;
*	     Pro_LabelInt(label, 1, 0, "FOO", -99, TRUE) returns 1000;
*	     Pro_LabelInt(label, 0, 0, "FOO", -99, TRUE) returns 1313;
*	     Pro_LabelInt(label, 1, 1, "BAR", -99, TRUE) returns 999;
*	     Pro_LabelInt(label, 1, 0, "BAR", -99, TRUE) returns 999;
*	     Pro_LabelInt(label, 0, 0, "BAR", -99, TRUE) raises
*			PRO_EVALUATION_FAILURE and returns -99;
*	     Pro_LabelInt(label, 0, 0, "BAR", -99, FALSE) returns -99;
*	     Pro_LabelInt(label, 0, 0, "ABC", -99, TRUE) raises
*			PRO_EVALUATION_FAILURE and returns -99;
*	     Pro_LabelInt(label, 0, 0, "ABC", -99, FALSE) returns -99;
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*	PRO_EVALUATION_FAILURE	raised optionally, if the named keyword is not
*				found or is not a valid integer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4		Pro_LabelInt(object, ntable, ncolumn, keyword, dfault,
				raise_error)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn, dfault;
RL_CHAR		*keyword;
RL_BOOL		raise_error;
{
ZPRO_COLUMN	*column;
ZPRO_TABLE	*table;
ZPRO_LABEL	*label;
RL_BOOL         status;
RL_INT4		value;

    if (ntable > 0) {

    /* Search column object if necessary */
	if (ncolumn > 0) {
	    column = ZPro_ColumnPtr(object, ntable, ncolumn);
	    if (column == NULL) return dfault;

	    status = ZPro_GetInt(column->odltree, keyword, &value);
	    if (status) return value;
	}

    /* Search table object if necessary */
	table = ZPro_TablePtr(object, ntable);
	if (table == NULL) return dfault;

        status = ZPro_GetInt(table->odltree, keyword, &value);
        if (status) return value;
    }

    /* Search root object if necessary */
    label = ZPro_LabelPtr(object);
    if (label == NULL) return dfault;

    status = ZPro_GetInt(label->odltree, keyword, &value);
    if (status) return value;

    /* Raise error if necessary */
    if (raise_error) {
	(void) sprintf(xpro_message, "\
keyword not found or not a valid integer\n\
label file = \"%s\"\n\
table = %d; column = %d; keyword = \"%s\"",
		label->filename, ntable, ncolumn, keyword);

	RL_RaiseError("PRO_EVALUATION_FAILURE", xpro_message);
    }

    return dfault;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelFloat (label.c)
*$ Abstract:
*	This routine returns the floating-point value of a specified keyword in
*	a PDS label.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_FLT8		Pro_LabelFloat(object, ntable, ncolumn, keyword, dfault,
*					raise_error)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable, ncolumn;
*	RL_CHAR		*keyword;
*	RL_FLT8		dfault;
*	RL_BOOL		raise_error;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable		table index, or 0 to search among top keywords only.
*	ncolumn		column index, or 0 to search among table keywords only.
*	keyword		keyword name.
*	dfault		default value to return on error.
*	raise_error	TRUE to raise PRO_EVALUATION_FAILURE on error;
*			FALSE otherwise.
*$ Outputs:
*	none
*$ Returns:
*	floating-point value of the keyword; default value on non-fatal error.
*$ Detailed_description:
*	This routine returns the floating-point value of a specified keyword in
*	a PDS label.
*
*	What section of the label is searched depends on the values of the
*	ntable and ncolumn parameters.  If ntable is zero, then only the
*	keywords at the top of the PDS label (above any PDS OBJECTs) are
*	searched.  If ntable is nonzero but ncolumn is zero, then the keywords
*	inside the TABLE/SERIES object but above the COLUMN objects are
*	searched, followed by the keywords at the top of the label.  If both
*	ntable and nseries are nonzero, then the keywords in the selected
*	COLUMN object are searched, followed by the keywords in the selected
*	TABLE/SERIES object, followed by the keywords at the top of the label.
*
*	If the keyword is not found in the PDS label, or is found but is not a
*	valid floating-point number, the default value is returned.  In this
*	case, error code PRO_EVALUATION_FAILURE if the raise_error parameter is
*	TRUE; otherwise no error condition is raised.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	FOO		= 313.
*	ABC		= "DEF"
*	OBJECT		= SERIES
*	  FOO		= 100
*	  BAR		= 999
*	  OBJECT	= COLUMN
*	    FOO		= 3.
*	  END_OBJECT	= COLUMN
*	END_OBJECT	= SERIES
*	END
*
*	Then Pro_LabelFloat(label, 1, 1, "FOO", -99., TRUE) returns 3.;
*	     Pro_LabelFloat(label, 1, 0, "FOO", -99., TRUE) returns 100.;
*	     Pro_LabelFloat(label, 0, 0, "FOO", -99., TRUE) returns 313.;
*	     Pro_LabelFloat(label, 1, 1, "BAR", -99., TRUE) returns 999.;
*	     Pro_LabelFloat(label, 1, 0, "BAR", -99., TRUE) returns 999.;
*	     Pro_LabelFloat(label, 0, 0, "BAR", -99., TRUE) raises
*			PRO_EVALUATION_FAILURE and returns -99.;
*	     Pro_LabelFloat(label, 0, 0, "BAR", -99., FALSE) returns -99.;
*	     Pro_LabelFloat(label, 0, 0, "ABC", -99., TRUE) raises
*			PRO_EVALUATION_FAILURE and returns -99.;
*	     Pro_LabelFloat(label, 0, 0, "ABC", -99., FALSE) returns -99.;
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*	PRO_EVALUATION_FAILURE	raised optionally, if the named keyword is not
*				found or is not a valid floating-point number.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_FLT8		Pro_LabelFloat(object, ntable, ncolumn, keyword, dfault,
		raise_error)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn;
RL_CHAR		*keyword;
RL_FLT8		dfault;
RL_BOOL		raise_error;
{
ZPRO_COLUMN	*column;
ZPRO_TABLE	*table;
ZPRO_LABEL	*label;
RL_BOOL         status;
RL_FLT8		value;

    if (ntable > 0) {

    /* Search column object if necessary */
	if (ncolumn > 0) {
	    column = ZPro_ColumnPtr(object, ntable, ncolumn);
	    if (column == NULL) return dfault;

	    status = ZPro_GetFloat(column->odltree, keyword, &value);
	    if (status) return value;
	}

    /* Search table object if necessary */
	table = ZPro_TablePtr(object, ntable);
	if (table == NULL) return dfault;

        status = ZPro_GetFloat(table->odltree, keyword, &value);
        if (status) return value;
    }

    /* Search root object if necessary */
    label = ZPro_LabelPtr(object);
    if (label == NULL) return dfault;

    status = ZPro_GetFloat(label->odltree, keyword, &value);
    if (status) return value;

    /* Raise error if necessary */
    if (raise_error) {
	(void) sprintf(xpro_message, "\
keyword not found or not a valid floating-point value\n\
label file = \"%s\"\n\
table = %d; column = %d; keyword = \"%s\"",
		label->filename, ntable, ncolumn, keyword);

	RL_RaiseError("PRO_EVALUATION_FAILURE", xpro_message);
    }

    return dfault;
}

/*
********************************************************************************
*$ Component_name:
*	Pro_LabelString (label.c)
*$ Abstract:
*	This routine returns a pointer to the character string value of the
*	specified keyword in a PDS label.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	C, PUBLIC, SUBROUTINE
*$ Declarations:
*	RL_CHAR		*Pro_LabelString(object, ntable, ncolumn, keyword,
*					dfault, raise_error)
*	PRO_OBJECT	*object;
*	RL_INT4		ntable, ncolumn;
*	RL_CHAR		*keyword, *dfault;
*	RL_BOOL		raise_error;
*$ Inputs:
*	object		pointer to the PDS label object.
*	ntable		table index, or 0 to search among top keywords only.
*	ncolumn		column index, or 0 to search among table keywords only.
*	keyword		keyword name.
*	dfault		default string to return on error.
*	raise_error	TRUE to raise PRO_EVALUATION_FAILURE on error;
*			FALSE otherwise.
*$ Outputs:
*	none
*$ Returns:
*	pointer to a (read-only) string containing the value of keyword; a
*	pointer to the default string on error.  The string is truncated if
*	necessary after 255 characters (plus the final '\0').
*$ Detailed_description:
*	This routine returns a pointer to the character string value of the
*	specified keyword in a PDS label.  Quotes (single or double) are
*	stripped away if present.
*
*	What section of the label is searched depends on the values of the
*	ntable and ncolumn parameters.  If ntable is zero, then only the
*	keywords at the top of the PDS label (above any PDS OBJECTs) are
*	searched.  If ntable is nonzero but ncolumn is zero, then the keywords
*	inside the TABLE/SERIES object but above the COLUMN objects are
*	searched, followed by the keywords at the top of the label.  If both
*	ntable and nseries are nonzero, then the keywords in the selected
*	COLUMN object are searched, followed by the keywords in the selected
*	TABLE/SERIES object, followed by the keywords at the top of the label.
*
*	If the keyword is not found in the PDS label, or is found but is not a
*	valid string, the default value is returned.  In this case, error code
*	PRO_EVALUATION_FAILURE if the raise_error parameter is TRUE; otherwise
*	no error condition is raised.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	FOO		= 313
*	OBJECT		= SERIES
*	  FOO		= ABC
*	  BAR		= 999
*	  OBJECT	= COLUMN
*	    FOO		= "Bar"
*	  END_OBJECT	= COLUMN
*	END_OBJECT	= SERIES
*	END
*
*	Then Pro_LabelString(label, 1, 1, "FOO", "xx", TRUE) returns "Bar";
*	     Pro_LabelString(label, 1, 0, "FOO", "xx", TRUE) returns "ABC";
*	     Pro_LabelString(label, 0, 0, "FOO", "xx", TRUE) returns "313";
*	     Pro_LabelString(label, 1, 1, "BAR", "xx", TRUE) returns "999";
*	     Pro_LabelString(label, 1, 0, "BAR", "xx", TRUE) returns "999";
*	     Pro_LabelString(label, 0, 0, "BAR", "xx", TRUE) raises
*			PRO_EVALUATION_FAILURE and returns "xx";
*	     Pro_LabelString(label, 0, 0, "BAR", "xx", FALSE) returns "xx";
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*	PRO_EVALUATION_FAILURE	raised optionally, if the named keyword is not
*				found or is not a valid string.
*$ Limitations:
*	Returned strings are limited to 255 characters, plus final null
*	character.
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_CHAR		*Pro_LabelString(object, ntable, ncolumn, keyword, dfault,
		raise_error)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn;
RL_CHAR		*keyword, *dfault;
RL_BOOL		raise_error;
{
ZPRO_COLUMN	*column;
ZPRO_TABLE	*table;
ZPRO_LABEL	*label;
RL_BOOL         status;

    /* Anticipate failure */
    strncpy(temp_name, dfault, TEMP_MAXLEN);
    temp_name[TEMP_MAXLEN] = '\0';

    if (ntable > 0) {

    /* Search column object if necessary */
	if (ncolumn > 0) {
	    column = ZPro_ColumnPtr(object, ntable, ncolumn);
	    if (column == NULL) return temp_name;

	    status = ZPro_GetString(column->odltree, keyword,
	                            temp_name, TEMP_MAXLEN);
	    if (status) return temp_name;
	}

    /* Search table object if necessary */
	table = ZPro_TablePtr(object, ntable);
	if (table == NULL) return temp_name;

        status = ZPro_GetString(table->odltree, keyword,
	                        temp_name, TEMP_MAXLEN);
        if (status) return temp_name;
    }

    /* Search root object if necessary */
    label = ZPro_LabelPtr(object);
    if (label == NULL) return temp_name;

    status = ZPro_GetString(label->odltree, keyword, temp_name, TEMP_MAXLEN);
    if (status) return temp_name;

    /* Raise error if necessary */
    if (raise_error) {
	(void) sprintf(xpro_message, "\
keyword not found or not a valid string\n\
label file = \"%s\"\n\
table = %d; column = %d; keyword = \"%s\"",
		label->filename, ntable, ncolumn, keyword);

	RL_RaiseError("PRO_EVALUATION_FAILURE", xpro_message);
    }

    return temp_name;
}

/*
********************************************************************************
* PROGRAMMER ROUTINES
********************************************************************************
* XPro_LabelInfo(object, ntable, ncolumn, tabletree, columntree,
*		rows, items, x1, x2, dx, xname, yname,
*		missing, invalid, offset, scale)
*
* This routine fetches information on a given table and column.
*
* Input:
*	object		PDS label object.
*	ntable		table index [1...n].
*	column		column index [1...n].
*
* Output:
*	*tabletree	pointer to the ODLTREE of the table.
*	*columntree	pointer to the ODLTREE of the column.
*	*rows		number of rows.
*	*items		number of items.
*	*x1, *x2, *dx	column sampling.
*	xname		name of the x-coordinate.
*	yname		name of the y-coordinate.
*	*missing	missing flag value.
*	*invalid	invalid flag value.
*	*offset		offset to apply to tabulated values.
*	*scale		scale factor to apply to tabulated values.
*
* Return:		TRUE if all is well; FALSE if an error occurred.
*
* Errors:
*	PRO_CLASS_ERROR		if object is NULL or not a PDS label object.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*******************************************************************************/

RL_BOOL XPro_LabelInfo(object, ntable, ncolumn, tabletree, columntree,
		rows, items, x1, x2, dx, xname, yname,
		missing, invalid, offset, scale)
PRO_OBJECT	*object;
RL_INT4		ntable, ncolumn, *rows, *items;
RL_FLT8		*x1, *x2, *dx, *missing, *invalid, *offset, *scale;
RL_VOID		**tabletree, **columntree;
RL_CHAR		**xname, **yname;
{
ZPRO_COLUMN	*column;
ZPRO_TABLE	*table;

    column = (ZPRO_COLUMN *) ZPro_ColumnPtr(object, ntable, ncolumn);
    if (column == NULL) return FALSE;

    *items	= column->items;
    *missing	= column->missing;
    *invalid	= column->invalid;
    *offset	= column->offset;
    *scale	= column->scale;
    *yname	= column->name;
    *columntree = (RL_VOID *) column->odltree;

    table = ZPro_TablePtr(object, ntable);
    *rows	= table->nrows;
    *x1		= table->x1;
    *x2		= table->x2;
    *dx		= table->dx;
    *xname	= table->xname;
    *tabletree	= (RL_VOID *) table->odltree;

    return TRUE;
}

/*
********************************************************************************
* INTERNAL ROUTINES
********************************************************************************
* ZPro_FreeLabel(pointer)
*
* This routine closes the PDS label and frees the data structures in memory
* associated with it.
*
* Input:
*	label		pointer to the PDS label object.
*
* Side Effects:		memory is freed.
*******************************************************************************/

static void	ZPro_FreeLabel(pointer)
RL_VOID		*pointer;
{
ZPRO_LABEL	*label;
RL_INT4		i, j;

    label = (ZPRO_LABEL *) pointer;
    if (label == NULL) return;

    if (label->tables != NULL) {
	for (i=0; i<label->ntables; i++) XRL_Free(label->tables[i].columns);
	XRL_Free(label->tables);
    }

    OdlFreeTree(label->odltree);
    XRL_Free(label);
}

/*
********************************************************************************
* ZPro_PrintLabel(pointer)
*
* This routine prints out information about a PDS label object.
*
* Input:
*	pointer		pointer to the PDS label object.
*******************************************************************************/

static void	ZPro_PrintLabel(pointer)
RL_VOID		*pointer;
{
ZPRO_LABEL	*label;
ZPRO_TABLE	*table;
ZPRO_COLUMN	*column;
RL_INT4		i, j;

    label = (ZPRO_LABEL *) pointer;

    /* Make sure object is not NULL */
    if (label == NULL) {
        printf("PRINT ERROR: PDS label pointer is NULL\n");
        return;
    }

    /* Make sure object is a PDS label */
    if (label->class.id != XPRO_LABEL_CLASS) {
        printf("PRINT ERROR: Object is not a PDS label\n");
        return;
    }

    /* Print object info... */
    for (i=1; i <= label->ntables; i++) {
	table = &(label->tables[i-1]);

	printf("\nTable #%d: %s\n", i, table->name);
	printf("  rows  = %d (%g to %g by %g)\n",
			table->nrows, table->x1, table->x2, table->dx);
	printf("  xname = %s\n", table->xname);

	for (j=1; j <= table->ncolumns; j++) {
	    column = &(table->columns[j-1]);

	    printf("  Column #%d: %s\n", j, column->name);
	    if (column->items != 1)
		printf("    items   = %d\n", column->items);
	    if (column->missing != -HUGE_VAL)
		printf("    missing = %g\n", column->missing);
	    if (column->invalid != -HUGE_VAL)
		printf("    invalid = %g\n", column->invalid);
	    if (column->offset != 0. || column->scale != 1.)
		printf("    scaling = %g * x + %g\n",
			column->scale, column->offset);
	}
    }
}

/*
********************************************************************************
* ZPro_LabelPtr(object)
*
* This internal routine returns a pointer to the object's ZPRO_LABEL data
* structure.
*
* Input:
*	object		pointer to a PDS label object.
*
* Return:		pointer to ZPRO_LABEL structure, or NULL on error.
*
* Errors:
*	PRO_CLASS_ERROR		if object is NULL or not a PDS label.
*******************************************************************************/

static ZPRO_LABEL	*ZPro_LabelPtr(object)
PRO_OBJECT		*object;
{
ZPRO_LABEL		*label;

    /* Find PDS label object pointer */
    label = (ZPRO_LABEL *) XPro_ObjectPtr(object);

    /* Make sure object is not NULL */
    if (label == NULL) {
	XPro_NullError("PDS label", object);
	return NULL;
    }

    /* Make sure object is a PDS label */
    if (label->class.id != XPRO_LABEL_CLASS) {
	XPro_ClassError("PDS label", object);
	return NULL;
    }

    return (ZPRO_LABEL *) object->class.pointer;
}

/*
********************************************************************************
* ZPro_TablePtr(object, ntable)
*
* This internal routine returns a pointer to the a selected ZPRO_TABLE data
* structure.
*
* Input:
*	object		pointer to a PDS label object.
*	ntable		table index [1...n].
*
* Return:		pointer to ZPRO_TABLE structure, or NULL on error.
*
* Errors:
*	PRO_CLASS_ERROR		if object is NULL or not a PDS label.
*	PRO_DOMAIN_ERROR	if the table index is out of range.
*******************************************************************************/

static ZPRO_TABLE	*ZPro_TablePtr(object, ntable)
PRO_OBJECT		*object;
RL_INT4			ntable;
{
ZPRO_LABEL		*label;

    /* Find PDS label object pointer */
    label = ZPro_LabelPtr(object);
    if (label == NULL) return NULL;

    /* Check the table index */
    if (ntable < 1 || ntable > label->ntables) {
	XPro_IDomainError("table index", object, 1, label->ntables, ntable);
	return NULL;
    }

    return &(label->tables[ntable-1]);
}

/*
********************************************************************************
* ZPro_ColumnPtr(object, ntable, ncolumn)
*
* This internal routine returns a pointer to a selected ZPRO_COLUMN data
* structure.
*
* Input:
*	object		pointer to a PDS label object.
*	ntable		table index [1...n].
*	ncolumn		column index [1...n].
*
* Return:		pointer to ZPRO_LABEL structure, or NULL on error.
*
* Errors:
*	PRO_CLASS_ERROR		if object is NULL or not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*******************************************************************************/

static ZPRO_COLUMN	*ZPro_ColumnPtr(object, ntable, ncolumn)
PRO_OBJECT		*object;
RL_INT4			ntable, ncolumn;
{
ZPRO_TABLE		*table;

    table = ZPro_TablePtr(object, ntable);
    if (table == NULL) return NULL;

    /* Check the column index */
    if (ncolumn < 1 || ncolumn > table->ncolumns) {
	XPro_IDomainError("column index", object, 1, table->ncolumns, ncolumn);
	return NULL;
    }

    return &(table->columns[ncolumn-1]);
}

/*
********************************************************************************
* ZPro_GetParam(odltree, name, ptr)
*
* This internal routine finds a specified keyword in a PDS object.
*
* Input:
*	odltree		pointer to the ODL tree.
*	name		name of keyword for which to search.
*	ptr		pointer where the resultant string is stored.
*
* Return:		TRUE if keyword is found; FALSE otherwise.
*******************************************************************************/

static RL_BOOL	ZPro_GetParam(odltree, name, ptr)
ODLTREE		odltree;
RL_CHAR		*name;
RL_CHAR		**ptr;
{
KEYWORD		*kwdptr;
RL_CHAR 	*string;

    kwdptr = OdlFindKwd(odltree, name, NULL, (unsigned long) 0,
		ODL_THIS_OBJECT);

    string = OdlGetKwdName(kwdptr);
    if (string == NULL) {
	*ptr = NULL;
	return FALSE;
    }

    *ptr = OdlGetKwdValue(kwdptr);
    return (*ptr != NULL);
}

/*
********************************************************************************
* ZPro_GetInt(odltree, name, value)
*
* This internal routine finds the integer value of a specified keyword in a PDS
* label.
*
* Input:
*	odltree		pointer to the object.
*	name		name of keyword for which to search.
*
* Output{
*	*value		value of integer, unchanged on error.
*
* Return:		TRUE if value is found and is a valid integer;
*			FALSE otherwise.
*******************************************************************************/

static RL_BOOL	ZPro_GetInt(odltree, name, value)
ODLTREE		odltree;
RL_CHAR		*name;
RL_INT4		*value;
{
RL_BOOL		status;
RL_CHAR		*string;
char		*ptr;
RL_INT4		rval;

    status = ZPro_GetParam(odltree, name, &string);
    if (!status) return FALSE;

    rval = strtol(string, &ptr, 10);

    if ((rval == 0) && (string == ptr)) {
	return FALSE;
    } else {
	*value = rval;
	return TRUE;
    }
}

/*
********************************************************************************
* ZPro_GetFloat(odltree, name, value)
*
* This internal routine finds the floating point value of a specified keyword in
* a PDS label.
*
* Input:
*	odltree		pointer to the object.
*	name		name of keyword for which to search.
*
* Output:
*	*value		floating-point value; unchanged on error.
*
* Return:		TRUE if value is found and is a valid float;
*			FALSE otherwise.
*******************************************************************************/

static RL_BOOL	ZPro_GetFloat(odltree, name, value)
ODLTREE		odltree;
RL_CHAR		*name;
RL_FLT8		*value;
{
RL_BOOL		status;
RL_CHAR		*string;
char		*ptr;
RL_FLT8		rval;

    status = ZPro_GetParam(odltree, name, &string);
    if (!status) {
	return FALSE;
    }

    rval = strtod(string, &ptr);

    if ((rval == 0.) && (string == ptr)) {
	return FALSE;
    } else {
	*value = rval;
	return TRUE;
    }
}

/*
********************************************************************************
* ZPro_GetString(odltree, name, value, maxlen)
*
* This internal routine finds the string value of a specified keyword in a PDS
* object.  It strips away surrounding quotes if necessary.
*
* Input:
*	odltree		pointer to the ODL tree.
*	name		name of keyword for which to search.
*	maxlen		maximum string length returned, excluding final '\0'.
*
* Output:
*	*value		string found, truncated if necessary; unchanged on
*			error.
*
* Return:		TRUE if value is found; FALSE otherwise.
*******************************************************************************/

static RL_BOOL	ZPro_GetString(odltree, name, value, maxlen)
ODLTREE		odltree;
RL_CHAR		*name, *value;
RL_INT4		maxlen;
{
RL_BOOL		status;
RL_CHAR		*string;
RL_INT4		len;

    status = ZPro_GetParam(odltree, name, &string);
    if ((!status) || (string == NULL)) {
	return FALSE;
    }

    if ((string[0] == '\"') || (string[0] == '\'')) {
	len = strlen(string) - 2;
	if (len	> maxlen) len = maxlen;
	strncpy(value, &string[1], len);
	value[len] = '\0';
    } else {
	strncpy(value, &string[0], maxlen);
	value[maxlen] = '\0';
    }

    return TRUE;
}

/*
********************************************************************************
* ZPro_DefInt(odltree, name, dfault, value)
*
* This internal routine finds the integer value of a specified keyword in a PDS
* label.  If the value is not found, it returns a default value.
*
* Input:
*	odltree		pointer to the object.
*	name		name of keyword for which to search.
*	dfault		default integer value.
*	*value		value of integer.
*******************************************************************************/

static void ZPro_DefInt(odltree, name, dfault, value)
ODLTREE	odltree;
RL_CHAR	*name;
RL_INT4	dfault, *value;
{
RL_BOOL	status;

    status = ZPro_GetInt(odltree, name, value);
    if (!status) *value = dfault;
}

/*
********************************************************************************
* ZPro_DefFloat(odltree, name, dfault, value)
*
* This internal routine finds the floating-point value of a specified keyword in
* a PDS label.  If the value is not found, it returns a default value.
*
* Input:
*	odltree		pointer to the object.
*	name		name of keyword for which to search.
*	dfault		default floating-point value.
*	*value		floating-point value found.
*******************************************************************************/

static void ZPro_DefFloat(odltree, name, dfault, value)
ODLTREE	odltree;
RL_CHAR	*name;
RL_FLT8	dfault, *value;
{
RL_BOOL	status;

    status = ZPro_GetFloat(odltree, name, value);
    if (!status) *value = dfault;
}

/*
********************************************************************************
* ZPro_DefFloat2(odltree, name1, name2, dfault, value)
*
* This internal routine finds the floating-point value of either of a specified
* pair of possible keywords in a PDS label.  If neither value is found, it
* returns a default value.
*
* Input:
*	odltree		pointer to the object.
*	name1, name2	names of keywords for which to search.
*	dfault		default floating-point value.
*	*value		floating-point value found.
*******************************************************************************/

static void ZPro_DefFloat2(odltree, name1, name2, dfault, value)
ODLTREE	odltree;
RL_CHAR	*name1, *name2;
RL_FLT8	dfault, *value;
{
RL_BOOL	status;

    status = ZPro_GetFloat(odltree, name1, value);
    if (status) return;

    ZPro_DefFloat(odltree, name2, dfault, value);
}

/*
********************************************************************************
* ZPro_DefString(odltree, name, dfault, value, maxlen)
*
* This internal routine finds the string value of a specified keyword in a PDS
* object.  It strips away surrounding quotes if necessary.  If the value is not
* found, a default string is returned.
*
* Input:
*	odltree		pointer to the ODL tree.
*	name		name of keyword for which to search.
*	*dfault		default string.
*	*value		string found, truncated if necessary.
*	maxlen		maximum string length returned, excluding final '\0'.
*******************************************************************************/

static void ZPro_DefString(odltree, name, dfault, value, maxlen)
ODLTREE	odltree;
RL_CHAR	*name, *dfault, *value;
RL_INT4	maxlen;
{
RL_BOOL	status;

    status = ZPro_GetString(odltree, name, value, maxlen);
    if (!status) {
	strncpy(value, dfault, maxlen);
	value[maxlen] = '\0';
    }
}

/*
********************************************************************************
* ZPro_DefName(odltree, name, unit, dfault, value, maxlen)
*
* This internal routine finds the string value of a specified keyword and
* merges it with an optional set of units in a PDS object.  It strips away any
* surrounding quotes if necessary.  If the value is not found, a default string
* is returned.
*
* Input:
*	odltree		pointer to the ODL tree.
*	name		name keyword for which to search.
*	name		unit keyword for which to search.
*	*dfault		default string.
*	*value		string found, truncated if necessary.
*	maxlen		maximum string length returned, excluding final '\0'.
*******************************************************************************/

static void ZPro_DefName(odltree, name, unit, dfault, value, maxlen)
ODLTREE		odltree;
RL_CHAR		*name, *unit, *dfault, *value;
RL_INT4		maxlen;
{
RL_CHAR		temp1[PRO_NAMELEN+1], temp2[PRO_NAMELEN+1];

    ZPro_DefString(odltree, name, dfault, temp1, PRO_NAMELEN);
    ZPro_DefString(odltree, unit, "N/A",  temp2, PRO_NAMELEN);

    if (strcmp(temp2, "N/A") == 0) {
	strncpy(value, temp1, maxlen);
    } else {
	(void) sprintf(xpro_message, "%s (%s)", temp1, temp2);
	strncpy(value, xpro_message, maxlen);
    }

    value[maxlen] = '\0';
}

/*
********************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
*$ Component_name:
*	FPro_OpenLabel (label.c, fprofile.for)
*$ Abstract:
*	This routine opens the specified PDS label file and creates a label
*	object.
*$ Keywords:
*	PROFILE, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_OpenLabel(labelfile)
*	character*(*)	labelfile
*$ Inputs:
*	labelfile	name of PDS label file.
*$ Outputs:
*	none
*$ Returns:
*	a FORTRAN pointer to the new label object.
*$ Detailed_description:
*	This routine opens the specified PDS label file and creates a label
*	object.  It parses the label to find the TABLE and SERIES objects.  Use
*	Pro_ColumnSeries() (see column.c) to create a series object from one of
*	the columns.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	Memory is allocated.  The label file is opened.
*$ Examples:
*	This snippet of code creates a label object for a PDS file called
*	"table.tab":
*
*	integer*4	label
*
*	label = FPro_OpenLabel('table.tab')
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_SETUP_FAILURE	if the label cannot be read or parsed or has no
*				TABLE or SERIES objects.
*$ Limitations:
*	file names are limited to 255 characters.
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_OpenLabel() defined here is the intermediary routine between
* FPro_OpenLabel() and Pro_OpenLabel(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C.  See fprofile.for for the
* rest of the code.
*
* integer*4 function GPro_OpenLabel(labelfile)
* byte		labelfile(*)
*******************************************************************************/

RL_INT4 FORTRAN_NAME(gpro_openlabel) (labelfile)
RL_CHAR *labelfile;
{
RL_VOID *ptr;
RL_INT4 index;

    /* Call function */
    ptr = (RL_VOID *) Pro_OpenLabel(labelfile);
    if (ptr == NULL) return 0;

    /* Save new pointer */
    index = FORT_AddPointer(ptr);
    if (index == 0) Pro_FreeObject((PRO_OBJECT *) ptr);

    return index;
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelCount (label.c)
*$ Abstract:
*	This routine returns the number of tables in a PDS label object, the
*	number of columns in a table, or the number of items in a column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_LabelCount(object, ntable, ncolumn)
*	integer*4 	object, ntable, ncolumn
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable		index of the selected table (0 or 1...n).
*	ncolumn		index of the selected column (0 or 1...n).
*$ Outputs:
*	none
*$ Returns:
*	If ntable is zero, this is the number of tables in the label;
*	If ncolumn is zero, this is the number of columns in the selected table;
*	Otherwise, this is the number of items in the selected column.
*$ Detailed_description:
*	This routine returns the number of tables in a PDS label object, the
*	number of columns in a table, or the number of items in a column.
*
*	The quantity returned depends on the which input parameters are set to
*	zero.  If ntable is zero, the total number of PDS TABLE and SERIES
*	objects is returned.  Otherwise, ntable is the index (starting with 1)
*	of the selected table.  If ncolumn is zero, the number of PDS COLUMN
*	objects in the selected table is returned.  Otherwise, ncolumn is the
*	index (starting with 1) of the selected column and the number of ITEMs
*	in the column is returned.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^TIME_SERIES	= "FOOBAR.TAB"
*	^DATA_TABLE	= "FOOBAR.DAT"
*	^IMAGE		= "FOOBAR.IMG"
*	OBJECT		= TIME_SERIES
*	  ... (3 columns defined, each with 1 item)
*	END_OBJECT	= TIME_SERIES
*	OBJECT		= DATA_TABLE
*	  ... (1 column defined, with 600 items)
*	END_OBJECT	= DATA_TABLE
*	OBJECT		= IMAGE
*	  ...
*	END_OBJECT	= IMAGE
*	END
*
*	Then FPro_LabelCount(label, 0, 0) returns 2 (number of tables/series);
*	     FPro_LabelCount(label, 1, 0) returns 3 (columns in TIME_SERIES);
*	     FPro_LabelCount(label, 1, 1) returns 1 (items in column);
*	     FPro_LabelCount(label, 2, 1) returns 600 (items in column);
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table or column index is out of range.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN object pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4 FORTRAN_NAME(fpro_labelcount) (object, ntable, ncolumn)
RL_INT4 *object;
RL_INT4 *ntable, *ncolumn;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) return 0;

    /* Call function */
    return Pro_LabelCount((PRO_OBJECT *) ptr, *ntable, *ncolumn);
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelName (label.c, fprofile.for)
*$ Abstract:
*	This routine returns the name of the specified table or column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	subroutine FPro_LabelName(object, ntable, ncolumn, name)
*	integer*4	object, ntable, ncolumn
*	character*(*)	name
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*	ncolumn		index of the selected column (0 or 1...n).
*$ Outputs:
*	name		desired name.  If ncolumn==0, this is the name of the
*			selected table; otherwise, this is the name of the
*			selected column.
*$ Returns:
*	none
*$ Detailed_description:
*	This routine returns the name of the specified table or column.
*
*	The quantity returned depends on the which input parameters are set to
*	zero.  If ncolumn is zero, the name of the PDS TABLE or SERIES object is
*	returned; otherwise, ncolumn is the index (starting with 1) of the
*	selected column and the routine returns the column's name.
*
*	COLUMN names are defined by the PDS label's NAME field, followed by a
*	space and (if present) the PDS label's UNIT field in parentheses.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  NAME				= OCCULTATION_GEOMETRY
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    NAME			= RECORD_INDEX
*	    UNIT			= 'N/A'
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    NAME			= INTERCEPT_RADIUS
*	    UNIT			= "km"
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then FPro_LabelName(label, 1, 0, name) sets name='OCCULTATION_GEOMETRY';
*	     FPro_LabelName(label, 1, 1, name) sets name='RECORD_INDEX';
*	     FPro_LabelName(label, 1, 2, name) sets name='INTERCEPT_RADIUS (km)'
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table or column index is out of range.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_LabelName() defined here is the intermediary routine between
* FPro_LabelName() and Pro_LabelName(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C.  See fprofile.for for the
* rest of the code.
*
* subroutine GPro_LabelName(object, ntable, ncolumn, buffer, lbuffer)
* integer*4	object, ntable, ncolumn, lbuffer
* byte		buffer(*)
*******************************************************************************/

void    FORTRAN_NAME(gpro_labelname) (object, ntable, ncolumn, buffer, lbuffer)
RL_INT4 *object, *ntable, *ncolumn, *lbuffer;
RL_CHAR *buffer;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) {
        buffer[0] = '\0';
        return;
    }

    /* Call function */
    strncpy(buffer, Pro_LabelName((PRO_OBJECT *) ptr, *ntable, *ncolumn),
            *lbuffer-1);
    buffer[*lbuffer-1] = '\0';
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelXName (label.c, fprofile.for)
*$ Abstract:
*	This routine returns the name of a table's X-coordinate.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	subroutine FPro_LabelXName(object, ntable, name)
*	integer*4	object, ntable
*	character*(*)	name
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*$ Outputs:
*	name		desired name, or blank on non-fatal error.
*$ Returns:
*	none
*$ Detailed_description:
*	This routine returns the name of a table's X-coordinate.  Names are
*	defined by the PDS label's SAMPLING_PARAMETER_NAME field, followed by a
*	space and (if present) the PDS label's SAMPLING_PARAMETER_UNIT field in
*	parentheses.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  NAME				= OCCULTATION_GEOMETRY
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    NAME			= RECORD_INDEX
*	    UNIT			= 'N/A'
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    NAME			= INTERCEPT_RADIUS
*	    UNIT			= "km"
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then FPro_LabelXName(label, 1, name) sets name='RECORD_INDEX'.
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table index is out of range.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_LabelXName() defined here is the intermediary routine between
* FPro_LabelXName() and Pro_LabelXName(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C.  See fprofile.for for the
* rest of the code.
*
* subroutine GPro_LabelXName(object, ntable, buffer, lbuffer)
* integer*4	object, ntable, lbuffer
* byte		buffer(*)
*******************************************************************************/

void    FORTRAN_NAME(gpro_labelxname) (object, ntable, buffer, lbuffer)
RL_INT4 *object, *ntable, *lbuffer;
RL_CHAR *buffer;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) {
        buffer[0] = '\0';
        return;
    }

    /* Call function */
    strncpy(buffer, Pro_LabelXName((PRO_OBJECT *) ptr, *ntable), *lbuffer-1);
    buffer[*lbuffer-1] = '\0';
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelFind (label.c, fprofile.for)
*$ Abstract:
*	This routine returns the index of the named table or column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_LabelFind(object, ntable, name)
*	integer*4	object, ntable
*	character*(*)	name
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*	name		name of the table or column to find.  For columns, this
*			string can optionally exclude units inside parentheses.
*$ Outputs:
*	none
*$ Returns:
*	index (1...n) of the selected table or column; 0 if not found.
*	If ntable is zero, this is the index of the table with the given name;
*	otherwise, this is the index of the column with the given name.
*$ Detailed_description:
*	This routine returns the index of the named table or column.
*
*	The quantity returned depends on the which input parameters are set to
*	zero.  If ntable is zero, the index (starting with 1) of the table with
*	the given name is returned.  Otherwise, ntable is the index of the
*	selected table and the number returned is the index of the column with
*	the given name.  For columns, if the name string does not contain units
*	(inside parentheses), then any units in the column name are ignored.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  NAME				= OCCULTATION_GEOMETRY
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    NAME			= RECORD_INDEX
*	    UNIT			= 'N/A'
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    NAME			= INTERCEPT_RADIUS
*	    UNIT			= "km"
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then FPro_LabelFind(label, 0, 'OCCULTATION_GEOMETRY')  returns 1;
*	     FPro_LabelFind(label, 1, 'RECORD_INDEX')          returns 1;
*	     FPro_LabelFind(label, 1, 'INTERCEPT_RADIUS (km)') returns 2;
*	     FPro_LabelFind(label, 1, 'INTERCEPT_RADIUS')      returns 2;
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table index is out of range.
*	PRO_NOT_FOUND		if no table or column of the given name exists.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_LabelFind() defined here is the intermediary routine between
* FPro_LabelFind() and Pro_LabelFind(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C.  See fprofile.for for the
* rest of the code.
*
* integer*4 function GPro_LabelFind(object, ntable, buffer)
* integer*4	object
* byte		buffer(*)
*******************************************************************************/

RL_INT4	FORTRAN_NAME(gpro_labelfind) (object, ntable, buffer)
RL_INT4 *object, *ntable;
RL_CHAR *buffer;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) return 0;

    /* Call function */
    return Pro_LabelFind((PRO_OBJECT *) ptr, *ntable, buffer);
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelSampling (label.c)
*$ Abstract:
*	This routine returns the sampling parameters used by a specified table
*	or column.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_LabelSampling(object, ntable, ncolumn, nitem,
*					x1, x2, dx)
*	integer*4	object, ntable, ncolumn, nitem
*	real*8		x1, x2, dx
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable          index of selected table (1...n).
*	ncolumn		index of the selected column (1...n) or 0 for row
*			sampling.
*	nitem		index of selected item, or 0 for all.
*$ Outputs:
*	x1		first sampling parameter.
*	x2		last sampling parameter.
*	dx		interval between samples.
*$ Returns:
*	the number of samples in selected column/item pair; 0 on error.
*$ Detailed_description:
*	This routine returns the sampling parameters used by a specified table
*	or column.
*
*	If nitem is nonzero, then it is assumed that the only a single item per
*	row is to be used; in this case the sampling parameters for the column
*	match those for the row.  However, if nitem is zero in a column that
*	contains multiple items, then the items are treated as subsamples, so
*	the sample interval is correspondingly reduced.
*
*	Note that the row sampling parameters are returned if ncolumn is zero.
*$ External_references:
*	Profile toolkit
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	^SERIES				= "PS1G02.TAB"
*	OBJECT				= SERIES
*	  SAMPLING_PARAMETER_NAME       = RECORD_INDEX
*	  SAMPLING_PARAMETER_UNIT       = 'N/A'
*	  START_SAMPLING_PARAMETER      = -16.
*	  STOP_SAMPLING_PARAMETER       = 1296.
*	  SAMPLING_PARAMETER_INTERVAL	= 1.0
*	  OBJECT			= COLUMN
*	    ITEMS			= 1
*	  END_OBJECT			= COLUMN
*	  OBJECT			= COLUMN
*	    ITEMS			= 100
*	  END_OBJECT			= COLUMN
*	END_OBJECT			= SERIES
*	END
*
*	Then FPro_LabelSampling(label, 1, 1, 1, x1, x2, dx)
*			sets x1=-16.d0, x2=1296.d0, dx=1.d0, returns 1313;
*	     FPro_LabelSampling(label, 1, 1, 0, x1, x2, dx)
*			sets x1=-16.d0, x2=1296.d0, dx=1.d0, returns 1313;
*	     FPro_LabelSampling(label, 1, 2, 1, x1, x2, dx)
*			sets x1=-16.d0, x2=1296.d0, dx=1.d0, returns 1313;
*	     FPro_LabelSampling(label, 1, 2, 0, x1, x2, dx)
*			sets x1=-16.d0, x2=1296.99d0, dx=0.01d0, returns 131300
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if table, column or item index is out of range.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN object pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
*******************************************************************************/

RL_INT4	FORTRAN_NAME(fpro_labelsampling) (object, ntable, ncolumn, nitem,
	                                  x1, x2, dx)
RL_INT4 *object, *ntable, *ncolumn, *nitem;
RL_FLT8	*x1, *x2, *dx;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) return 0;

    /* Call function */
    return Pro_LabelSampling((PRO_OBJECT *) ptr, *ntable, *ncolumn, *nitem,
                             x1, x2, dx);
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelInt (label.c, fprofile.for)
*$ Abstract:
*	This routine returns the integer value of a specified keyword in a PDS
*	label.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	integer*4 function FPro_LabelInt(object, table, column,
*					keyword, default, raise_error)
*	integer*4	object, table, column, default
*	character*(*)	keyword
*	logical*4	raise_error
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable		table index, or 0 to search among top keywords only.
*	ncolumn		column index, or 0 to search among table keywords only.
*	keyword		keyword name.
*	dfault		default value to return on error.
*	raise_error	.TRUE. to raise PRO_EVALUATION_FAILURE on error;
*			.FALSE. otherwise.
*$ Outputs:
*	none
*$ Returns:
*	integer value of the keyword; default value on non-fatal error.
*$ Detailed_description:
*	This routine returns the integer value of a specified keyword in a PDS
*	label.
*
*	What section of the label is searched depends on the values of the
*	ntable and ncolumn parameters.  If ntable is zero, then only the
*	keywords at the top of the PDS label (above any PDS OBJECTs) are
*	searched.  If ntable is nonzero but ncolumn is zero, then the keywords
*	inside the TABLE/SERIES object but above the COLUMN objects are
*	searched, followed by the keywords at the top of the label.  If both
*	ntable and nseries are nonzero, then the keywords in the selected
*	COLUMN object are searched, followed by the keywords in the selected
*	TABLE/SERIES object, followed by the keywords at the top of the label.
*
*	If the keyword is not found in the PDS label, or is found but is not a
*	valid integer, the default value is returned.  In this case, error code
*	PRO_EVALUATION_FAILURE if the raise_error parameter is TRUE; otherwise
*	no error condition is raised.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	FOO		= 1313
*	ABC		= "DEF"
*	OBJECT		= SERIES
*	  FOO		= 1000
*	  BAR		= 999
*	  OBJECT	= COLUMN
*	    FOO		= 3
*	  END_OBJECT	= COLUMN
*	END_OBJECT	= SERIES
*	END
*
*	Then FPro_LabelInt(label, 1, 1, 'FOO', -99, .TRUE.) returns 3;
*	     FPro_LabelInt(label, 1, 0, 'FOO', -99, .TRUE.) returns 1000;
*	     FPro_LabelInt(label, 0, 0, 'FOO', -99, .TRUE.) returns 1313;
*	     FPro_LabelInt(label, 1, 1, 'BAR', -99, .TRUE.) returns 999;
*	     FPro_LabelInt(label, 1, 0, 'BAR', -99, .TRUE.) returns 999;
*	     FPro_LabelInt(label, 0, 0, 'BAR', -99, .TRUE.) raises
*			PRO_EVALUATION_FAILURE and returns -99;
*	     FPro_LabelInt(label, 0, 0, 'BAR', -99, .FALSE.) returns -99;
*	     FPro_LabelInt(label, 0, 0, 'ABC', -99, .TRUE.) raises
*			PRO_EVALUATION_FAILURE and returns -99;
*	     FPro_LabelInt(label, 0, 0, 'ABC', -99, .FALSE.) returns -99;
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*	PRO_EVALUATION_FAILURE	raised optionally, if the named keyword is not
*				found or is not a valid integer.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_LabelInt() defined here is the intermediary routine between
* FPro_LabelInt() and Pro_LabelInt(), allowing for the fact that strings cannot
* be passed directly between FORTRAN and C.  See fprofile.for for the rest of
* the code.
*
* integer*4 function GPro_LabelInt(object, ntable, ncolumn, keyword,
*					default, raise_error)
* integer*4	object, ntable, ncolumn, default
* byte		keyword(*)
* logical*4	raise_error
*******************************************************************************/

RL_INT4 FORTRAN_NAME(gpro_labelint) (object, ntable, ncolumn, keyword, dfault,
                                     raise_error)
RL_INT4 *object, *ntable, *ncolumn, *dfault, *raise_error;
RL_CHAR *keyword;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) return 0;

    /* Call function */
    return Pro_LabelInt((PRO_OBJECT *) ptr, *ntable, *ncolumn, keyword,
                        *dfault, (RL_BOOL) *raise_error);
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelFloat (label.c, fprofile.for)
*$ Abstract:
*	This routine returns the floating-point value of a specified keyword in
*	a PDS label.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	real*8 function FPro_LabelFloat(object, table, column,
*					keyword, default, raise_error)
*	integer*4 	object, table, column
*	character*(*)	keyword
*	real*8		default
*	logical*4	raise_error
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable		table index, or 0 to search among top keywords only.
*	ncolumn		column index, or 0 to search among table keywords only.
*	keyword		keyword name.
*	dfault		default value to return on error.
*	raise_error	.TRUE. to raise PRO_EVALUATION_FAILURE on error;
*			.FALSE. otherwise.
*$ Outputs:
*	none
*$ Returns:
*	floating-point value of the keyword; default value on non-fatal error.
*$ Detailed_description:
*	This routine returns the floating-point value of a specified keyword in
*	a PDS label.
*
*	What section of the label is searched depends on the values of the
*	ntable and ncolumn parameters.  If ntable is zero, then only the
*	keywords at the top of the PDS label (above any PDS OBJECTs) are
*	searched.  If ntable is nonzero but ncolumn is zero, then the keywords
*	inside the TABLE/SERIES object but above the COLUMN objects are
*	searched, followed by the keywords at the top of the label.  If both
*	ntable and nseries are nonzero, then the keywords in the selected
*	COLUMN object are searched, followed by the keywords in the selected
*	TABLE/SERIES object, followed by the keywords at the top of the label.
*
*	If the keyword is not found in the PDS label, or is found but is not a
*	valid floating-point number, the default value is returned.  In this
*	case, error code PRO_EVALUATION_FAILURE if the raise_error parameter is
*	TRUE; otherwise no error condition is raised.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	FOO		= 313.
*	ABC		= "DEF"
*	OBJECT		= SERIES
*	  FOO		= 100
*	  BAR		= 999
*	  OBJECT	= COLUMN
*	    FOO		= 3.
*	  END_OBJECT	= COLUMN
*	END_OBJECT	= SERIES
*	END
*
*	Then FPro_LabelFloat(label, 1, 1, 'FOO', -99.d0, .TRUE.) returns 3.d0;
*	     FPro_LabelFloat(label, 1, 0, 'FOO', -99.d0, .TRUE.) returns 100.d0;
*	     FPro_LabelFloat(label, 0, 0, 'FOO', -99.d0, .TRUE.) returns 313.d0;
*	     FPro_LabelFloat(label, 1, 1, 'BAR', -99.d0, .TRUE.) returns 999.d0;
*	     FPro_LabelFloat(label, 1, 0, 'BAR', -99.d0, .TRUE.) returns 999.d0;
*	     FPro_LabelFloat(label, 0, 0, 'BAR', -99.d0, .TRUE.) raises
*			PRO_EVALUATION_FAILURE and returns -99.d0;
*	     FPro_LabelFloat(label, 0, 0, 'BAR', -99.d0, .FALSE.) returns -99.d0;
*	     FPro_LabelFloat(label, 0, 0, 'ABC', -99.d0, .TRUE.) raises
*			PRO_EVALUATION_FAILURE and returns -99.d0;
*	     FPro_LabelFloat(label, 0, 0, 'ABC', -99.d0, .FALSE.) returns -99.d0;
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*	PRO_EVALUATION_FAILURE	raised optionally, if the named keyword is not
*				found or is not a valid floating-point number.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN pointer.
*$ Limitations:
*	none
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_LabelFloat() defined here is the intermediary routine between
* FPro_LabelFloat() and Pro_LabelFloat(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C.  See fprofile.for for the
* rest of the code.
*
* real*8 function GPro_LabelFloat(object, ntable, ncolumn, keyword,
*					default, raise_error)
* integer*4	object, ntable, ncolumn, raise_error
* byte		keyword(*)
* real*8	default
*******************************************************************************/

RL_FLT8 FORTRAN_NAME(gpro_labelfloat) (object, ntable, ncolumn, keyword,
                                       dfault, raise_error)
RL_INT4 *object, *ntable, *ncolumn, *raise_error;
RL_CHAR *keyword;
RL_FLT8	*dfault;
{
RL_VOID *ptr;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) return 0.;

    /* Call function */
    return Pro_LabelFloat((PRO_OBJECT *) ptr, *ntable, *ncolumn, keyword,
                          *dfault, (RL_BOOL) *raise_error);
}

/*
********************************************************************************
*$ Component_name:
*	FPro_LabelString (label.c, fprofile.for)
*$ Abstract:
*	This routine returns a pointer to the character string value of the
*	specified keyword in a PDS label.
*$ Keywords:
*	PROFILE, SERIES, LABEL
*	FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
*	subroutine FPro_LabelString(object, table, column,
*					keyword, default, raise_error, value)
*	integer*4	object, table, column
*	character*(*)	keyword, default, value
*	logical*4	raise_error
*$ Inputs:
*	object		FORTRAN pointer to the PDS label object.
*	ntable		table index, or 0 to search among top keywords only.
*	ncolumn		column index, or 0 to search among table keywords only.
*	keyword		keyword name.
*	dfault		default string to return on error.
*	raise_error	.TRUE. to raise PRO_EVALUATION_FAILURE on error;
*			.FALSE. otherwise.
*$ Outputs:
*	value		string containing the value of keyword; default string
*			on error.  The string is truncated if necessary after
*			255 characters.
*$ Returns:
*	none
*$ Detailed_description:
*	This routine returns a pointer to the character string value of the
*	specified keyword in a PDS label.  Quotes (single or double) are
*	stripped away if present.
*
*	What section of the label is searched depends on the values of the
*	ntable and ncolumn parameters.  If ntable is zero, then only the
*	keywords at the top of the PDS label (above any PDS OBJECTs) are
*	searched.  If ntable is nonzero but ncolumn is zero, then the keywords
*	inside the TABLE/SERIES object but above the COLUMN objects are
*	searched, followed by the keywords at the top of the label.  If both
*	ntable and nseries are nonzero, then the keywords in the selected
*	COLUMN object are searched, followed by the keywords in the selected
*	TABLE/SERIES object, followed by the keywords at the top of the label.
*
*	If the keyword is not found in the PDS label, or is found but is not a
*	valid string, the default value is returned.  In this case, error code
*	PRO_EVALUATION_FAILURE if the raise_error parameter is TRUE; otherwise
*	no error condition is raised.
*$ External_references:
*	Profile toolkit, Object Access library.
*$ Side_effects:
*	none
*$ Examples:
*	Suppose the PDS label associated with an object looks like this:
*
*	FOO		= 313
*	OBJECT		= SERIES
*	  FOO		= ABC
*	  BAR		= 999
*	  OBJECT	= COLUMN
*	    FOO		= "Bar"
*	  END_OBJECT	= COLUMN
*	END_OBJECT	= SERIES
*	END
*
*	Then FPro_LabelString(label, 1, 1, 'FOO', 'xx', .TRUE., value)
*			sets value = 'Bar';
*	     FPro_LabelString(label, 1, 0, 'FOO', 'xx', .TRUE., value)
*			sets value = 'ABC';
*	     FPro_LabelString(label, 0, 0, 'FOO', 'xx', .TRUE., value)
*			sets value = '313';
*	     FPro_LabelString(label, 1, 1, 'BAR', 'xx', .TRUE., value)
*			sets value = '999';
*	     FPro_LabelString(label, 1, 0, 'BAR', 'xx', .TRUE., value)
*			sets value = '999';
*	     FPro_LabelString(label, 0, 0, 'BAR', 'xx', .TRUE., value)
*			raises PRO_EVALUATION_FAILURE and sets value = 'xx';
*	     FPro_LabelString(label, 0, 0, 'BAR', 'xx', .FALSE., value)
*			sets value = 'xx'.
*$ Error_handling:
*	Profile library error handling is in effect.
*
*	Conditions raised:
*	PRO_CLASS_ERROR		if object is NULL or is not a PDS label.
*	PRO_DOMAIN_ERROR	if the table/column index is out of range.
*	PRO_EVALUATION_FAILURE	raised optionally, if the named keyword is not
*				found or is not a valid string.
*	FORTRAN_POINTER_ERROR	if object is not a valid FORTRAN pointer.
*$ Limitations:
*	Returned strings are limited to 255 characters.
*$ Author_and_institution:
*	Mark R. Showalter
*	NASA/Ames Research Center
*$ Version_and_date:
*	1.0: March 1998
*$ Change_history:
*	none
********************************************************************************
* Note: GPro_LabelString() defined here is the intermediary routine between
* FPro_LabelString() and Pro_LabelString(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C.  See fprofile.for for the
* rest of the code.
*
* subroutine GPro_LabelString(object, ntable, ncolumn, keyword, default,
*					raise_error, buffer, lbuffer)
* integer*4	object, ntable, ncolumn, lbuffer, raise_error
* byte		keyword(*), buffer(*), default(*)
*******************************************************************************/

void FORTRAN_NAME(gpro_labelstring) (object, ntable, ncolumn, keyword, dfault,
                                     raise_error, buffer, lbuffer)
RL_INT4 *object, *ntable, *ncolumn, *raise_error, *lbuffer;
RL_CHAR *keyword, *dfault, *buffer;
{
RL_VOID *ptr;
RL_CHAR	*string;

    /* Look up label pointer */
    ptr = FORT_GetPointer(*object);
    if (ptr == NULL) {
	buffer[0] = '\0';
	return;
    }

    /* Call function */
    string = Pro_LabelString((PRO_OBJECT *) ptr, *ntable, *ncolumn, keyword,
                             dfault, (RL_BOOL) *raise_error);

    strncpy(buffer, string, *lbuffer-1);
    buffer[*lbuffer] = '\0';
}

/*******************************************************************************
*/