/*========================================================================*/
/*                                                                        */
/*                         PDS Label Library Lite                         */
/*                               (lablib3)                                */
/*                                                                        */
/*  Version:                                                              */
/*                                                                        */
/*      1.0Beta    Mar 31, 1994                                           */
/*      1.0        Jan 23, 1995                                           */
/*      1.1        Feb 23, 1995                                           */
/*      1.2        Jun 06, 1995  Preliminary                              */
/*                                                                        */
/*  Change History:                                                       */
/*                                                                        */
/*      03-31-94    Original code                                         */
/*      01-09-95    jsh - Changed OBJECT to OBJDESC                       */
/*      01-09-95    jsh - Corrected strcmp and strncmp == NULL            */
/*      02-16-95    jsh - Applied LASP changes (from OA dvlp - s. monk)   */
/*                      - Function Prototypes                             */
/*                      - Filename Units                                  */
/*                      - Filename for SUN/Unix                           */
/*                      - TB_MAX_BUFFER                                   */
/*                      - Several 0 -> NULL in function calls             */
/*      02-20-95    jsh - Added OdlPrintLine (reduced # fprintf)          */
/*      06-06-95    jsh/gmw - Stop gap for "/*" in text strings           */
/*      06-06-95    jsh/gmw - Allow SFDU without "= SFDU"                 */
/*      12-11-95    sm/LASP - Allow SFDU without "= SFDU"                 */
/*                            (Removed previous change, added new one.)   */
/*                          - Replaced 'class' by 'class_name' for C++    */
/*                            compatibility.                              */
/*                          - Added global variable odl_errno, and error  */
/*                            codes.                                      */
/*                          - Included oamalloc.h, and replaced 'malloc'  */
/*                            by 'OaMalloc'.                              */
/*                          - OdlValidElement: fixed dropping of trailing */
/*                            '>' from units expressions.                 */
/*                          - OdlDataType: '(' for ODL_SEQUENCE, '{' for  */
/*                            ODL_SET.                                    */
/*                          - OdlExpandLabelFile:                         */
/*                              - propagate 'suppress_messages' input     */
/*                                argument to OdlParseFile.               */
/*                              - move keywords resulting from expansions */
/*                                to immediately after the ^STRUCTURE or  */
/*                                ^CATALOG keyword, which is left as is.  */
/*                              - removed call to ExpandIsRecursive.      */
/*                              - added parameter to OdlGetFileSpec call. */
/*                          - OdlParseFile:
/*                              - added new input arg 'label_str', and    */
/*                                code to allow parsing a label string.   */
/*                              - fixed bug involving comments embedded   */
/*                                inside text strings.                    */
/*                          - OdlParseLabelString: modified to use new    */
/*                            OdlParseFile instead of creating tmp file.  */
/*                          - OdlCutObjDesc, OdlPasteObjDescBefore,       */
/*                            OdlPasteObjDescAfter: added robustness by   */
/*                            checking inputs for NULL.                   */
/*                          - OdlPrintKeywords: print SFDU keyword value  */
/*                            ("= SFDU_LABEL") if keyword value exists.   */
/*                          - OdlGetFileSpec: filled in the code for what */
/*                            was previously a stub;  added new function  */
/*                            OdlCheckFileSpec.                           */
/*                          - OdlPrintLabel: print label regardless of    */
/*                            value of odl_suppress_messages.             */
/*      02-13-95    sm/LASP - Changed TB_MAX_BUFFER in toolbox.h from     */
/*                            32767 to 30000 because some compilers don't */
/*                            allow more than 32K of local data.          */
/*      02-04-97    sm/LASP - Modified OdlParseFile and OdlPrintLabel to  */
/*                            parse, store and print pre-comments; added  */
/*                            keyword value alignment to OdlPrintKeywords */
/*      05-19-96    sm/LASP - Modified OdlGetFile to parse Macintosh-style*/
/*                            paths with spaces or other non-ASCII chars. */
/*      05-21-97    sm/LASP - added bug fix by CSC/John Kerich to do      */
/*                            start of line check in OdlWildCardCompare.  */
/*      01-29-98    sm/LASP - Added routines to get and set comments and  */
/*                            filename fields in OBJDESC and KEYWORD      */
/*                            structures.                                 */
/*      01-30-98    sm/LASP - Added argument 'options' to OdlPrintLabel   */
/*                            allow printing of expanded ^STRUCTURE nodes */
/*                            to a separate file, or to suppress printing */
/*                            of "END" statement.  Changed opening of     */
/*                            output file for append to opening for write.*/
/*                            Added user routine OdlChangeExpansionFile.  */
/*                            Added internal routines OdlChangeExpandFile */
/*                            and OdlExtractExpansionTree.                */
/*                                                                        */
/*========================================================================*/

#include "lablib3.h"

long odl_message_count = {0};
short odl_suppress_messages = {FALSE};
int odl_errno;



/*========================================================================*/
/*                                                                        */
/*                          Label Parse routines                          */
/*                                                                        */
/*========================================================================*/



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlParseLabelFile                                               */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      WARNING!  Do not use this routine unless you never plan to read */
/*      to read a variable-length record file - this routine will not   */
/*      work!   Use the Object Access Library routine OaParseLabelFile  */
/*      instead, as it handles all record types.                        */
/*                                                                      */
/*      This routine causes a file containing a PDS label to be parsed  */
/*      and its "^" keywords expanded.  It returns a pointer to the     */
/*      root of the OBJECT tree data structure.                         */
/*                                                                      */
/*      The "filespec" parameter is the full path and name of the       */
/*      label file to be parsed.                                        */
/*                                                                      */
/*      The "message_fname" parameter is the name of the file where     */
/*      parser error messages will be written.  If the file exists,     */
/*      the messages are appended, otherwise a new file is created.     */
/*      A NULL value passed in causes messages to be sent to stdout.    */
/*                                                                      */
/*      The "suppress_messages" parameter is a flag that tells the code */
/*      whether or not to print parser error messages.  A value of TRUE */
/*      (1) tells the code to supress all messages.  If this parameter  */
/*      is 1, it doesn't matter what you specified with the             */
/*      "message_fname".  Nothing will be written to that file.  If a   */
/*      zero (0) is passed in, then messages will be written to the     */
/*      file you specified with the "message_fname" parameter.          */
/*                                                                      */
/*      The expand parameter is a flag that controls whether or not     */
/*      ^STRUCTURE and ^CATALOG keywords are expanded.  To expand one   */
/*      of these keywords means to take the file name it points to,     */
/*      parse its contents, and insert the results into the tree right  */
/*      after the keyword.  The keyword itself is left unchanged.       */
/*      In other words, these keywords function just like include files */
/*      in "C".  These are the values that can be passed in:            */
/*                                                                      */
/*             ODL_EXPAND_STRUCTURE - expand ^STRUCTURE keywords only   */
/*             ODL_EXPAND_CATALOG   - expand ^CATALOG keywords only     */
/*             ODL_EXPAND_STRUCTURE | ODL_EXPAND_CATALOG - expand       */
/*                   both keywords (the "|" character is the logical    */
/*                   "or" of both values).                              */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*      WARNING:  The value returned by this routine points to memory   */
/*                allocated by this routine (sometimes quite a bit of   */
/*                memory!).  Be sure to deallocate it using the         */
/*                OdlFreeTree routine.                                  */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlParseLabelFile (filespec, message_fname, expand, suppress_messages)

char *filespec;
char *message_fname;
MASK expand;
unsigned short suppress_messages;

#else

OBJDESC *OdlParseLabelFile (char *filespec, char *message_fname, MASK expand,
                           unsigned short suppress_messages)

#endif

{
    OBJDESC *root = {NULL};
   
    odl_suppress_messages = suppress_messages;
    root = (OBJDESC *) OdlParseFile(filespec,NULL,NULL,message_fname,NULL,
                                    suppress_messages,1,1,0);
    root = (OBJDESC *) OdlExpandLabelFile(root, message_fname, expand,
                                            suppress_messages);

    return(root);

}  /*  End:  "OdlParseLabelFile"  */




/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlParseLabelString                                             */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - Modified to use new OdlParseFile instead  */
/*                            of creating a temporary file.             */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine causes a character string containing an ODL        */
/*      statement to be parsed and its "^" keywords expanded.  It       */
/*      returns a pointer to the root of the OBJECT tree data structure.*/
/*                                                                      */
/*      WARNING:  The value returned by this routine points to memory   */
/*                allocated by this routine.  Be sure to deallocate it  */
/*                using the OdlFreeTree routine.                        */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlParseLabelString (odl_string, message_fname,
                             expand, suppress_messages)

char *odl_string;
char *message_fname;
MASK expand;
unsigned short suppress_messages;

#else

OBJDESC *OdlParseLabelString (char *odl_string, char *message_fname,
                             MASK expand, unsigned short suppress_messages)

#endif
{
    OBJDESC *root = {NULL};

    odl_suppress_messages = suppress_messages;

    root = (OBJDESC *) OdlParseFile(NULL,NULL,odl_string,message_fname,NULL,
                                    suppress_messages,1,1,0);
    root = (OBJDESC *) OdlExpandLabelFile(root, message_fname, expand,
                                            suppress_messages);
    return(root);

}  /*  End:  "OdlParseLabelString"  */




/*========================================================================*/
/*                                                                        */
/*                        Label Expand routines                           */
/*                                                                        */
/*========================================================================*/

/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlExpandLabelFile                                              */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - Pass suppress_messages to OdlParseFile.   */
/*      12-11-95    sm/LASP - Move keywords resulting from expansions   */
/*                            to immediately after the ^STRUCTURE or    */
/*                            ^CATALOG keyword, instead of after the    */
/*                            last keyword.                             */
/*                          - Removed call to ExpandIsRecursive.        */
/*                          - Added parameter to OdlGetFileSpec call.   */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine locates "^STRUCTURE" and "^CATALOG" keywords and   */
/*      expands them.  To "expand" means to extract the file name       */
/*      pointed to by the keyword, parse its contents, and insert the   */
/*      resulting tree into the label right after the keyword.          */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlExpandLabelFile (object, message_fname, expand, suppress_messages)

OBJDESC *object;
char *message_fname;
MASK expand;
unsigned short suppress_messages;

#else

OBJDESC *OdlExpandLabelFile (OBJDESC *object, char *message_fname,
                             MASK expand, unsigned short suppress_messages)

#endif
{
    KEYWORD *kwd = {NULL};
    KEYWORD *new_kwd = {NULL};
    KEYWORD *save_kwd = {NULL};
    OBJDESC *temp_root = {NULL};
    OBJDESC *new_obj = {NULL};
    OBJDESC *save_obj = {NULL};
    FILE *l_ptr = {NULL};
    unsigned long start_loc = {1};
    unsigned short loc_type = {ODL_RECORD_LOCATION};
    unsigned short done = {FALSE};
    char *fspec = {NULL};
    char *fname = {NULL};
    char *keyword_search_string = {NULL};
    char *fudge_string = {"_TEMPORARILY_RENAMED"};
    char error_message[5*(TB_MAXLINE + TB_MAXPATH + TB_MAXFNAME)];

    odl_suppress_messages = suppress_messages;

    /*  Let's expand all ^STRUCTURE keywords, shall we?  */
    if ((expand&ODL_EXPAND_STRUCTURE) == ODL_EXPAND_STRUCTURE)
    {
        expand -= ODL_EXPAND_STRUCTURE;
        CopyString(keyword_search_string, "^*STRUCTURE")
    }
    else
    /*  On second thought, let's expand all ^CATALOG keywords  */
    if ((expand&ODL_EXPAND_CATALOG) == ODL_EXPAND_CATALOG)
    {
        expand -= ODL_EXPAND_CATALOG;
        CopyString(keyword_search_string, "^*CATALOG")
    }
    else
    /*  Hmmm. I guess we have nothing left to expand.  */
    {
        expand = ODL_NOEXPAND;
        done = TRUE;
    }

    /*  Keep expanding until we can expand no more forever  */
    while (! done)
    {
        /*  Find the expand keyword wherever it my be hiding  */
        kwd = (KEYWORD *) OdlFindKwd(object, keyword_search_string,
                                     NULL, 1, ODL_RECURSIVE_DOWN);

        /*  We're done if there aren't any more keywords to expand  */
        if (kwd == NULL)
            done = TRUE;
        else
        {
            /*  Get the file name from the "^*STRUCTURE" or "^*CATALOG" keyword
                value, minus quotes and blanks, sans path  */
            fname = (char *) OdlGetFileName(kwd, &start_loc, &loc_type);
            /*  Figure out exactly where the file is located  */
            fspec = (char *) OdlGetFileSpec(fname, kwd->file_name,
                                            keyword_search_string);

            /*  We're in trouble if we can't find the file  */
            if (fspec == NULL)
            {
                sprintf(error_message,
                        "Unable to locate %s file:  %s",
                        keyword_search_string, fname);
                odl_errno = 200;
                OdlPrintMessage(message_fname,NULL,kwd->line_number,
                                error_message);
            }
            else
            {
                l_ptr = (FILE *) OdlLocateStart(fspec, start_loc, loc_type);

  
                /*  Parse the file  */
                temp_root = (OBJDESC *) OdlParseFile(fspec,l_ptr,NULL,
                                                     message_fname,NULL,
                                                     suppress_messages,1,1,
                                                     1);
                   
                /*  Was there anything in the file to parse?  */
                if (temp_root != NULL)
                {
                    /*  Move keywords from the temporary root node to just
                        after the ^STRUCTURE or ^CATALOG keyword.  */
                    for (new_kwd=temp_root->last_keyword;
                             new_kwd != NULL; new_kwd = save_kwd)
                    {
                        save_kwd = new_kwd->left_sibling;
                        OdlPasteKwdAfter((KEYWORD *) OdlCutKwd(new_kwd),
                                          kwd);
                    }

                    /*  Move sub-objects of the temporary root node to
                        under the ^STRUCTURE or ^CATALOG keyword's node. */
                    for (new_obj=temp_root->first_child;
                         new_obj != NULL; new_obj = save_obj)
                    {
                        save_obj = new_obj->right_sibling;
                        OdlPasteObjDesc( OdlCutObjDesc(new_obj),
                                         kwd->parent);
                    }

                    /*  Deallocate the temporary root  */
                    temp_root->first_keyword = NULL;
                    temp_root->first_child = NULL;
                    temp_root = (OBJDESC *) OdlFreeTree(temp_root);

                }  /*  End:  "if (temp_root != NULL) ..."  */
   
                /*  Free the file spec storage  */
                LemmeGo(fspec)

            }  /*  End:  "if (fspec == NULL) ... else ..."  */

            /* Temporarily rename the ^STRUCTURE or ^CATALOG keyword so
               it won't be found again at the top of the loop (and thus
               expanded again).  After the loop we'll change it back
               to its original value.  sm 2/4/97 */

            AppendString( kwd->name, fudge_string);

            /*  Free the file name storage  */
            LemmeGo(fname)
           
        }  /*  End:  "if (kwd == NULL) ... else ..."  */

    }  /*  End:  "while (! done) ..."  */

    /* Restore any keyword names changed above to their original values. */
    AppendString( keyword_search_string, fudge_string)
    while (1)
    {
        kwd = (KEYWORD *) OdlFindKwd(object, keyword_search_string,
                                     NULL, 1, ODL_RECURSIVE_DOWN);
        if (kwd == NULL)  break;

        kwd->name[ strlen( kwd->name) - strlen( fudge_string)] = '\0';
        strcpy( error_message, kwd->name);
        LemmeGo( kwd->name)
        CopyString( kwd->name, error_message)
    }

    /*  Free the keyword search string  */
    LemmeGo(keyword_search_string)

    /*  Repeat this whole process for the next type of EXPAND keyword,
        if another was specified.  E.g. we just expanded all the
        ^STRUCTURE keywords, now we'll expand all the ^CATALOG keywords.  */
    if (expand != ODL_NOEXPAND)
    {
        object = (OBJDESC *) OdlExpandLabelFile(object, message_fname,
                                               expand, suppress_messages);
    }

    /*  Return the root of the expanded tree  */
    return(object);

}  /*  End:  "OdlExpandLabelFile"  */



/*******************/
/*  Local Routine  */
/*******************/

#ifdef _NO_PROTO

static unsigned short ExpandIsRecursive (keyword, exp_fname)

KEYWORD *keyword;
char *exp_fname;

#else

static unsigned short ExpandIsRecursive (KEYWORD *keyword, char *exp_fname)

#endif
{
    OBJDESC *obj = {NULL};
    char *temp_fname = {NULL};
    unsigned short found = {FALSE};

    if ((keyword != NULL) && (exp_fname != NULL))
    {
#if (defined( VAX) || defined( ALPHA_VMS))
        UpperCase(exp_fname)
#endif

        CopyString(temp_fname, keyword->file_name)

#if (defined( VAX) || defined( ALPHA_VMS))
        UpperCase(temp_fname)
#endif

        found = (strcmp(temp_fname, exp_fname) == 0);
        LemmeGo(temp_fname)

        for (obj=keyword->parent;
                  ((! found) && (obj != NULL)); obj=obj->parent)
        {
            CopyString(temp_fname, obj->file_name)

#if (defined( VAX) || defined( ALPHA_VMS))
            UpperCase(temp_fname)
#endif

            found = (strcmp(temp_fname, exp_fname) == 0);
            LemmeGo(temp_fname)
        }
    }

    return(found);

}  /*  End:  "ExpandIsRecursive"  */





/*========================================================================*/
/*                                                                        */
/*                     Object description routines                        */
/*                                                                        */
/*========================================================================*/

/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlFindObjDesc                                                  */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine locates an object within a parsed label by its     */
/*      class name (like TABLE), by its position (look for the seventh  */
/*      table object in the label), by a particular keyword present     */
/*      in the object (like NAME), or by a particular value that a      */
/*      particular keyword has (like START_BYTE = 76).                  */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlFindObjDesc(start_object, object_class, keyword_name,
                       keyword_value, object_position, search_scope)

OBJDESC *start_object;
char *object_class;
char *keyword_name;
char *keyword_value;
unsigned long object_position;
unsigned short search_scope;

#else

OBJDESC *OdlFindObjDesc( OBJDESC *start_object, char *object_class,
                        char *keyword_name, char *keyword_value,
                        unsigned long object_position,
                        unsigned short search_scope)

#endif
{
    OBJDESC *found_object = {NULL};
    OBJDESC *obj = {NULL};
    KEYWORD *kwd = {NULL};
    unsigned short found = {FALSE};
    unsigned short scope = {search_scope};
    unsigned long current_position = {0};

    for (obj=start_object;
          ((obj != NULL) && (! found));
            obj = (OBJDESC *) OdlNextObjDesc(obj, start_object->level, &scope))
    {
        if (object_class == NULL)
             found = TRUE;
        else
             found = OdlWildCardCompare(object_class, obj->class_name);

        if ((found) && (keyword_name != NULL))
        {
            kwd = (KEYWORD *) OdlFindKwd(obj, keyword_name,
                                         NULL, 1, ODL_THIS_OBJECT);
            found = (kwd != NULL);
        }

        if ((found) && (keyword_value != NULL))
            found = OdlWildCardCompare(keyword_value, (char *) OdlGetKwdValue(kwd));

        if ((found) && (object_position > 0))
            found = ((++current_position) == object_position);

        if (found) found_object = obj;   

    }  /*  End:  "for (obj=start_object; ..."  */

    return(found_object);

}  /*  End:  "OdlFindObjDesc"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlNextObjDesc                                                  */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine locates the next object in the tree based on the   */
/*      search_scope passed in.                                         */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlNextObjDesc (object, root_level, search_scope)

OBJDESC *object;
unsigned long root_level;
unsigned short *search_scope;

#else

OBJDESC *OdlNextObjDesc (OBJDESC *object, unsigned long root_level,
                        unsigned short *search_scope)

#endif
{
    OBJDESC *next_object = {NULL};

    if (object != NULL)
    {
        switch (*search_scope)
        {
            /*  look only in the current object  */
            case ODL_THIS_OBJECT    :  next_object = NULL;
                                       break;

            /*  look at the current object's first child now, and its   */
            /*  child's right siblings in subsequent searches           */
            case ODL_CHILDREN_ONLY  :  next_object = object->first_child;
                                       *search_scope = ODL_SIBLINGS_ONLY;
                                       break;

            /*  look at the current object's right sibling  */
            case ODL_SIBLINGS_ONLY  :  next_object = object->right_sibling;
                                       break;

            /*  treat the current object as the root of a sub-tree  */
            case ODL_RECURSIVE_DOWN :  next_object = (OBJDESC *) OdlTraverseTree(object, root_level);
                                       break;

            /*  search children, then siblings, then move up to parent  */
            /*  keep going until the end of the label is reached        */
            default                 :  next_object = (OBJDESC *)
                                                      OdlTraverseTree(object,
                                                                     (unsigned long) 0);
                                       break;

        }  /*  End:  "switch (*search_scope) ..."  */

    }  /*  End:  "if (object != NULL) ..."  */

    return(next_object);

}  /*  End:  "OdlNextObjDesc"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlCutObjDesc                                                   */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - prevent crash when object has no parent.  */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine cuts an ODL object structure out of an ODL tree    */
/*      and returns a pointer to it.  All references to it in the tree  */
/*      are removed, and references within the object to its parent and */
/*      siblings in the original tree are removed.  The object retains  */
/*      its children, if any.                                           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlCutObjDesc (object)

OBJDESC *object;

#else

OBJDESC *OdlCutObjDesc (OBJDESC *object)

#endif
{
    if (object != NULL)
    {
        if (object->parent == NULL)
            return( object);

        if (object->right_sibling == NULL)
            object->parent->last_child = object->left_sibling;
        else
            object->right_sibling->left_sibling = object->left_sibling;

        if (object->left_sibling == NULL)
            object->parent->first_child = object->right_sibling;
        else
            object->left_sibling->right_sibling = object->right_sibling;

        object->parent = NULL;
        object->left_sibling = NULL;
        object->right_sibling = NULL;

    }  /*  End:  "if (object != NULL) ..."  */
    else
        odl_errno = 1;

    return(object);

}  /*  End routine:  "OdlCutObjDesc"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlPasteObjDesc                                                 */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine adds an object to a tree as the last child of the  */
/*      parent_object.                                                  */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlPasteObjDesc (new_object, parent_object)

OBJDESC *new_object;
OBJDESC *parent_object;

#else

OBJDESC *OdlPasteObjDesc (OBJDESC *new_object, OBJDESC *parent_object)

#endif
{
    if ((new_object != NULL) && (parent_object != NULL))
    {
        new_object->left_sibling = parent_object->last_child;
        new_object->right_sibling = NULL;
        new_object->parent = parent_object;
   
        if (parent_object->first_child == NULL)
            parent_object->first_child = new_object;

        if (parent_object->last_child != NULL)
            parent_object->last_child->right_sibling = new_object;

        parent_object->last_child = new_object;

        OdlAdjustObjDescLevel(new_object);

    }  /*  End:  "if ((new_object != NULL) && ..."  */
    else
        odl_errno = 1;

    return(new_object);

}  /*  End routine:  "OdlPasteObjDesc"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlPasteObjDescBefore                                           */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - Return NULL if old_object has no parent.  */
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine adds an object to a tree as the left sibling of    */
/*      the old_object.                                                 */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlPasteObjDescBefore (new_object, old_object)

OBJDESC *new_object;
OBJDESC *old_object;

#else

OBJDESC *OdlPasteObjDescBefore (OBJDESC *new_object, OBJDESC *old_object)

#endif
{
    if ((new_object != NULL) && (old_object != NULL))
    {
        if (old_object->parent == NULL)
        {
            odl_errno = 10;
            return( NULL);
        }

        new_object->left_sibling = old_object->left_sibling;
        new_object->right_sibling = old_object;
        new_object->parent = old_object->parent;
   
        if (old_object->left_sibling == NULL)
            old_object->parent->first_child = new_object;
        else
            old_object->left_sibling->right_sibling = new_object;

        old_object->left_sibling = new_object;

        OdlAdjustObjDescLevel(new_object);

    }  /*  End:  "if ((new_object != NULL) && ..."  */
    else
        odl_errno = 1;
   
    return(new_object);

}  /*  End routine:  "OdlPasteObjDescBefore"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlPasteObjDescAfter                                            */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - Return NULL if old_object has no parent.  */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine adds an object to a tree as the right sibling of   */
/*      the old_object.                                                 */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlPasteObjDescAfter (new_object, old_object)

OBJDESC *new_object;
OBJDESC *old_object;

#else

OBJDESC *OdlPasteObjDescAfter (OBJDESC *new_object, OBJDESC *old_object)

#endif
{
    if ((new_object != NULL) && (old_object != NULL))
    {
        if (old_object->parent == NULL)
        {
            odl_errno = 10;
            return( NULL);
        }

        new_object->right_sibling = old_object->right_sibling;
        new_object->left_sibling = old_object;
        new_object->parent = old_object->parent;
   
        if (old_object->right_sibling == NULL)
            old_object->parent->last_child = new_object;
        else
            old_object->right_sibling->left_sibling = new_object;

        old_object->right_sibling = new_object;

        OdlAdjustObjDescLevel(new_object);

    }  /*  End:  "if ((new_object != NULL) && ..."  */
    else
        odl_errno = 1;

    return(new_object);

}  /*  End routine:  "OdlPasteObjDescAfter"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlCopyObjDesc                                                  */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine makes a copy of an object and returns a pointer    */
/*      to the copy.  All fields are duplicated except for references   */
/*      to the original tree, which are removed.                        */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlCopyObjDesc (object)

OBJDESC *object;

#else

OBJDESC *OdlCopyObjDesc (OBJDESC *object)

#endif
{
    OBJDESC *new_object = {NULL};

    if (object != NULL)
    {
        new_object = OdlNewObjDesc(object->class_name,
                               object->pre_comment, object->line_comment,
                               object->post_comment, object->end_comment,
                               object->file_name, object->is_a_group,
                               object->line_number);
    }
    else
        odl_errno = 1;
   
    return(new_object);

}  /*  End routine:  "OdlCopyObjDesc"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlNewObjDesc                                                   */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - Replaced 'malloc' by 'OaMalloc'.          */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine creates a new object structure and initializes     */
/*      its fields with the values passed in.                           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlNewObjDesc (object_class, pre_comment, line_comment, post_comment,
                       end_comment, file_name, is_a_group, line_number)
char *object_class;
char *pre_comment;
char *line_comment;
char *post_comment;
char *end_comment;
char *file_name;
short is_a_group;
long line_number;

#else

OBJDESC *OdlNewObjDesc (char *object_class, char *pre_comment,
                       char *line_comment, char *post_comment,
                       char *end_comment, char *file_name, short is_a_group,
                       long line_number)

#endif
{
    OBJDESC *new_object = {NULL};

    if ((new_object = (OBJDESC *)OaMalloc((long) sizeof(OBJDESC))) == NULL)
    {
        odl_errno = 50;
        SayGoodbye()
    }
    else
    {
        CopyString(new_object->class_name, object_class)
        CopyString(new_object->pre_comment, pre_comment)
        CopyString(new_object->line_comment, line_comment)
        CopyString(new_object->post_comment, post_comment)
        CopyString(new_object->end_comment, end_comment)
        CopyString(new_object->file_name, file_name)

        new_object->is_a_group = is_a_group;
        new_object->child_count = 0;
        new_object->line_number = line_number;
        new_object->level = 0;
        new_object->parent = NULL;
        new_object->left_sibling = NULL;
        new_object->right_sibling = NULL;
        new_object->first_child = NULL;
        new_object->last_child = NULL;
        new_object->first_keyword = NULL;
        new_object->last_keyword = NULL;
        new_object->appl1 = NULL;
        new_object->appl2 = NULL;

    }  /*  End:  "if ((new_object = ... else ..."  */

    return(new_object);

}  /*  End routine:  "OdlNewObjDesc"  */




/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetLabelVersion                                              */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns a pointer to a character string containing */
/*      the ODL version of the label.  It looks for this information in */
/*      the ODL_VERSION_NUMBER keyword.                                 */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO THE ACTUAL VALUE OF THE         */
/*                ODL_VERSION_NUMBER KEYWORD AND MUST NOT BE FREED.     */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

char *OdlGetLabelVersion (object)

OBJDESC *object;

#else

char *OdlGetLabelVersion (OBJDESC *object)

#endif
{
    KEYWORD *kwd = {NULL};
    char *version = {NULL};

    if (object != NULL)
    {
        kwd = (KEYWORD *) OdlFindKwd(object, "PDS_VERSION_ID",
                                     NULL, 1, ODL_THIS_OBJECT);
        if (kwd == NULL)
        {
            kwd = (KEYWORD *) OdlFindKwd(object, "ODL_VERSION_NUMBER",
                                         NULL, 1, ODL_THIS_OBJECT);
        }

        if (kwd != NULL)
            version = (char *) OdlGetKwdValue(kwd);
    }
    else
        odl_errno = 1;

    return(version);

}  /*  End:  "OdlGetLabelVersion"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetObjDescClassName                                          */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns the class name of an object.               */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO THE ACTUAL INFORMATION STORED   */
/*                IN THE ODL OBJECT STRUCTURE AND MUST NOT BE FREED.    */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

char *OdlGetObjDescClassName (object)

OBJDESC *object;

#else

char *OdlGetObjDescClassName (OBJDESC *object)

#endif
{
    char *class_name = {NULL};

    if (object != NULL)
        class_name = object->class_name;
    else
        odl_errno = 1;

    return(class_name);

}  /*  End:  "OdlGetObjDescClassName"  */



/************************************************************************/
/*                                                                      */
/*  Components:                                                         */
/*                                                                      */
/*      OdlGetObjPreComment                                             */
/*      OdlGetObjLineComment                                            */
/*      OdlGetObjPostComment                                            */
/*      OdlGetObjEndComment                                             */
/*      OdlGetObjFilename                                               */
/*      OdlGetKwdPreComment                                             */
/*      OdlGetKwdLineComment                                            */
/*      OdlGetKwdFilename                                               */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      Steve Monk (Laboratory for Atmospheric and Space Physics)       */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    January 20, 1998                                         */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      01-20-98    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      These routines return a specific comment string or filename     */
/*      string from an object or keyword structure.                     */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THESE ROUTINES.  THE RETURN */
/*                VALUE OF EACH ROUTINE IS A POINTER TO THE ACTUAL      */
/*                INFORMATION STORED IN THE ODL OBJECT OR KEYWORD       */
/*                STRUCTURE AND MUST NOT BE FREED.                      */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO
char *OdlGetObjPreComment (object)
OBJDESC *object;
#else
char *OdlGetObjPreComment (OBJDESC *object)
#endif
{
    char *str = {NULL};
    if (object != NULL)
        str = object->pre_comment;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetObjPreComment"  */

#ifdef _NO_PROTO
char *OdlGetObjLineComment (object)
OBJDESC *object;
#else
char *OdlGetObjLineComment (OBJDESC *object)
#endif
{
    char *str = {NULL};
    if (object != NULL)
        str = object->line_comment;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetObjLineComment"  */

#ifdef _NO_PROTO
char *OdlGetObjPostComment (object)
OBJDESC *object;
#else
char *OdlGetObjPostComment (OBJDESC *object)
#endif
{
    char *str = {NULL};
    if (object != NULL)
        str = object->post_comment;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetObjPostComment"  */


#ifdef _NO_PROTO
char *OdlGetObjEndComment (object)
OBJDESC *object;
#else
char *OdlGetObjEndComment (OBJDESC *object)
#endif
{
    char *str = {NULL};
    if (object != NULL)
        str = object->end_comment;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetObjEndComment"  */



#ifdef _NO_PROTO
char *OdlGetObjFilename (object)
OBJDESC *object;
#else
char *OdlGetObjFilename (OBJDESC *object)
#endif
{
    char *str = {NULL};
    if (object != NULL)
        str = object->file_name;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetObjFilename"  */


#ifdef _NO_PROTO
char *OdlGetKwdPreComment (keyword)
KEYWORD *keyword;
#else
char *OdlGetKwdPreComment (KEYWORD *keyword)
#endif
{
    char *str = {NULL};
    if (keyword != NULL)
        str = keyword->pre_comment;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetKwdPreComment"  */

#ifdef _NO_PROTO
char *OdlGetKwdLineComment (keyword)
KEYWORD *keyword;
#else
char *OdlGetKwdLineComment (KEYWORD *keyword)
#endif
{
    char *str = {NULL};
    if (keyword != NULL)
        str = keyword->line_comment;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetKwdLineComment"  */

#ifdef _NO_PROTO
char *OdlGetKwdFilename (keyword)
KEYWORD *keyword;
#else
char *OdlGetKwdFilename (KEYWORD *keyword)
#endif
{
    char *str = {NULL};
    if (keyword != NULL)
        str = keyword->file_name;
    else
        odl_errno = 1;
    return(str);
}  /*  End:  "OdlGetKwdFilename"  */



/************************************************************************/
/*                                                                      */
/*  Components:                                                         */
/*                                                                      */
/*      OdlSetObjPreComment                                             */
/*      OdlSetObjLineComment                                            */
/*      OdlSetObjPostComment                                            */
/*      OdlSetObjEndComment                                             */
/*      OdlSetObjFilename                                               */
/*      OdlSetKwdPreComment                                             */
/*      OdlSetKwdLineComment                                            */
/*      OdlSetKwdFilename                                               */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      Steve Monk (Laboratory for Atmospheric and Space Physics)       */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    January 20, 1998                                         */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      01-20-98    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      These routines set a specific comment string or filename string */
/*      in an object or keyword structure.  The input string is copied  */
/*      before setting it as the new value.  The old string is freed.   */
/*      If the input string is NULL, the corresponding string in the    */
/*      object or keyword structure is set to NULL after freeing the    */
/*      old string.                                                     */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO
void OdlSetObjPreComment (object, pre_comment)
OBJDESC *object;
char *pre_comment;
#else
void OdlSetObjPreComment (OBJDESC *object, char *pre_comment)
#endif
{
    char *str = {NULL};
    if (pre_comment != NULL)
        CopyString( str, pre_comment)
    LemmeGo( object->pre_comment)
    object->pre_comment = str;
    return;
}  /*  End:  "OdlSetObjPreComment"  */

#ifdef _NO_PROTO
void OdlSetObjLineComment (object, line_comment)
OBJDESC *object;
char *line_comment;
#else
void OdlSetObjLineComment (OBJDESC *object, char *line_comment)
#endif
{
    char *str = {NULL};
    if (line_comment != NULL)
        CopyString( str, line_comment)
    LemmeGo( object->line_comment)
    object->line_comment = str;
    return;
}  /*  End:  "OdlSetObjLineComment"  */

#ifdef _NO_PROTO
void OdlSetObjPostComment (object, post_comment)
OBJDESC *object;
char *post_comment;
#else
void OdlSetObjPostComment (OBJDESC *object, char *post_comment)
#endif
{
    char *str = {NULL};
    if (post_comment != NULL)
        CopyString( str, post_comment)
    LemmeGo( object->post_comment)
    object->post_comment = str;
    return;
}  /*  End:  "OdlSetObjPostComment"  */


#ifdef _NO_PROTO
void OdlSetObjEndComment (object, end_comment)
OBJDESC *object;
char *end_comment;
#else
void OdlSetObjEndComment (OBJDESC *object, char *end_comment)
#endif
{
    char *str = {NULL};
    if (end_comment != NULL)
        CopyString( str, end_comment)
    LemmeGo( object->end_comment)
    object->end_comment = str;
    return;
}  /*  End:  "OdlSetObjEndComment"  */

#ifdef _NO_PROTO
void OdlSetObjFilename (object, filename)
OBJDESC *object;
char *filename;
#else
void OdlSetObjFilename (OBJDESC *object, char *filename)
#endif
{
    char *str = {NULL};
    if (filename != NULL)
        CopyString( str, filename)
    LemmeGo( object->file_name)
    object->file_name = str;
    return;
}  /*  End:  "OdlSetObjFilename"  */

#ifdef _NO_PROTO
void OdlSetKwdPreComment (keyword, pre_comment)
KEYWORD *keyword;
char *pre_comment;
#else
void OdlSetKwdPreComment (KEYWORD *keyword, char *pre_comment)
#endif
{
    char *str = {NULL};
    if (pre_comment != NULL)
        CopyString( str, pre_comment)
    LemmeGo( keyword->pre_comment)
    keyword->pre_comment = str;
    return;
}  /*  End:  "OdlSetKwdPreComment"  */

#ifdef _NO_PROTO
void OdlSetKwdLineComment (keyword, line_comment)
KEYWORD *keyword;
char *line_comment;
#else
void OdlSetKwdLineComment (KEYWORD *keyword, char *line_comment)
#endif
{
    char *str = {NULL};
    if (line_comment != NULL)
        CopyString( str, line_comment)
    LemmeGo( keyword->line_comment)
    keyword->line_comment = str;
    return;
}  /*  End:  "OdlSetKwdLineComment"  */

#ifdef _NO_PROTO
void OdlSetKwdFilename (keyword, filename)
KEYWORD *keyword;
char *filename;
#else
void OdlSetKwdFilename (KEYWORD *keyword, char *filename)
#endif
{
    char *str = {NULL};
    if (filename != NULL)
        CopyString( str, filename)
    LemmeGo( keyword->file_name)
    keyword->file_name = str;
    return;
}  /*  End:  "OdlSetKwdFilename"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetObjComments                                               */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      Steve Monk (Laboratory for Atmospheric and Space Physics)       */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    January 20, 1998                                         */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      01-20-98    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns the comment strings from an object struct. */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THESE ROUTINES.             */
/*                EACH OUTPUT VALUE IS A POINTER TO THE ACTUAL          */
/*                INFORMATION STORED IN THE ODL STRUCTURE AND MUST NOT  */
/*                BE FREED.                                             */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO
void OdlGetObjComments (object, pre_comment, line_comment, post_comment,
                        end_comment)
OBJDESC *object;
char **pre_comment;
char **line_comment;
char **post_comment;
char **end_comment;
#else
void OdlGetObjComments (OBJDESC *object, char **pre_comment,
                        char **line_comment, char **post_comment,
                        char **end_comment)
#endif
{
    *pre_comment = OdlGetObjPreComment( object);
    *line_comment = OdlGetObjLineComment( object);
    *post_comment = OdlGetObjPostComment( object);
    *end_comment = OdlGetObjEndComment( object);
    return;
}  /*  End:  "OdlGetObjComments"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlSetObjComments                                               */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      Steve Monk (Laboratory for Atmospheric and Space Physics)       */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    January 20, 1998                                         */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      01-20-98    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine sets the comment strings of an object structure to */
/*      the corresponding input strings.  Each input string is copied   */
/*      before setting it as a new comment value.  The old comment      */
/*      strings are freed.  If an input string is NULL, the             */
/*      corresponding comment string in the object structure is set to  */
/*      NULL.                                                           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO
void OdlSetObjComments (object, pre_comment, line_comment, post_comment,
                        end_comment)
OBJDESC *object;
char *pre_comment;
char *line_comment;
char *post_comment;
char *end_comment;
#else
void OdlSetObjComments (OBJDESC *object, char *pre_comment,
                        char *line_comment, char *post_comment,
                        char *end_comment)
#endif
{
    OdlSetObjPreComment( object, pre_comment);
    OdlSetObjLineComment( object, line_comment);
    OdlSetObjPostComment( object, post_comment);
    OdlSetObjEndComment( object, end_comment);
    return;
}  /*  End:  "OdlGetObjComments"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetObjDescChildCount
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns a count of the immediate children of an    */
/*      object.  It does not count children of children, etc.           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

int OdlGetObjDescChildCount (object)

OBJDESC *object;

#else

int OdlGetObjDescChildCount (OBJDESC *object)

#endif
{
    OBJDESC *obj = {NULL};
    int child_count = {0};

    if (object != NULL)
    {
        for (obj=object->first_child; obj != NULL; obj=obj->right_sibling)
            ++child_count;
    }
    else
        odl_errno = 1;

    return(child_count);

}  /*  End:  "OdlGetObjDescChildCount"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetObjDescLevel                                              */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns the nesting level of an object.  The ROOT  */
/*      object in a tree is always defined to be level 0.               */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

int OdlGetObjDescLevel (object)

OBJDESC *object;

#else

int OdlGetObjDescLevel (OBJDESC *object)

#endif
{
    OBJDESC *obj = {NULL};
    int level = {0};

    if (object != NULL)
    {
        for (obj=object->parent; obj != NULL; obj=obj->parent)
            ++level;
    }
    else
        odl_errno = 1;

    return(level);

}  /*  End:  "OdlGetObjDescLevel"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlAdjustObjDescLevel                                           */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine changes the nesting level of an object and all of  */
/*      its subobjects so they fit in with their place in the overall   */
/*      ODL tree.  This is particularly useful when objects are cut     */
/*      from one tree and pasted into another tree, perhaps higher or   */
/*      lower in the nesting hierarchy then they were in the original   */
/*      tree.                                                           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

void OdlAdjustObjDescLevel (object)

OBJDESC *object;

#else

void OdlAdjustObjDescLevel (OBJDESC *object)

#endif
{
    OBJDESC *obj = {NULL};
    unsigned short scope = {ODL_RECURSIVE_DOWN};

    if (object == NULL)
    {
        odl_errno = 1;
        return;
    }

    for (obj=object; obj != NULL;
             obj = (OBJDESC *) OdlNextObjDesc(obj, object->level, &scope))
    {
        obj->level = (obj->parent == NULL) ? 0 : (1 + obj->parent->level);
    }

    return;

}  /*  End routine:  "OdlAdjustObjDescLevel"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetObjDescParent                                             */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns a pointer to an object's parent.           */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO AN EXISTING ODL OBJECT AND      */
/*                AND MUST NOT BE FREED.                                */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

OBJDESC *OdlGetObjDescParent (object)

OBJDESC *object;

#else

OBJDESC *OdlGetObjDescParent (OBJDESC *object)

#endif
{
    OBJDESC *parent = {NULL};

    if (object != NULL)
        parent = object->parent;

    return(parent);

}  /*  End:  "OdlGetObjDescParent"  */




/*========================================================================*/
/*                                                                        */
/*                           Keyword routines                             */
/*                                                                        */
/*========================================================================*/


/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlFindKwd                                                      */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine locates the keyword in a label that satisfies the  */
/*      requirements passed in:  The object where the search is to      */
/*      begin, the name of the keyword, a particular value that the     */
/*      keyword must have, which version of the keyword we want (if     */
/*      there are duplicates), and the search scope we want to use to   */
/*      limit the objects searched.                                     */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlFindKwd (start_object, keyword_name, keyword_value,
                         keyword_position, search_scope)
OBJDESC *start_object;
char *keyword_name;
char *keyword_value;
unsigned long keyword_position;
unsigned short search_scope;

#else

KEYWORD *OdlFindKwd (OBJDESC *start_object, char *keyword_name,
                     char *keyword_value, unsigned long keyword_position,
                     unsigned short search_scope)

#endif
{
    OBJDESC *obj = {NULL};
    KEYWORD *kwd = {NULL};
    KEYWORD *found_kwd = {NULL};
    unsigned short found = {FALSE};
    unsigned short scope = {search_scope};
    unsigned long current_position = {0};

    if (start_object == NULL)
    {
        odl_errno = 1;
        return( NULL);
    }

    for (obj=start_object;
            ((obj != NULL) && (! found));
                obj = (OBJDESC *) OdlNextObjDesc(obj, start_object->level, &scope))
    {
        for (kwd=obj->first_keyword; ((kwd != NULL) && (! found)); kwd=kwd->right_sibling)
        {
            if (keyword_name == NULL)
                found = TRUE;
            else
                found = OdlWildCardCompare(keyword_name, kwd->name);

            if ((found) && (keyword_value != NULL))
                found = OdlWildCardCompare(keyword_value, (char *) OdlGetKwdValue(kwd));
   
            if ((found) && (keyword_position > 0))
                found = ((++current_position) == keyword_position);
   
            if (found) found_kwd = kwd;   

        }  /*  End:  "for (kwd=obj-> ..."  */

    }  /*  End:  "for (obj=start_object; ..."  */

    return(found_kwd);

}  /*  End:  "OdlFindKwd"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlNextKwd                                                      */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine locates the keyword in a label that satisfies the  */
/*      requirements passed in:  The object where the search is to      */
/*      begin, the name of the keyword, a particular value that the     */
/*      keyword must have, which version of the keyword we want (if     */
/*      there are duplicates), and the search scope we want to use to   */
/*      limit the objects searched.                                     */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlNextKwd (start_keyword, keyword_name, keyword_value,
                     keyword_position, search_scope)

KEYWORD *start_keyword;
char *keyword_name;
char *keyword_value;
unsigned long keyword_position;
unsigned short search_scope;

#else

KEYWORD *OdlNextKwd (KEYWORD *start_keyword, char *keyword_name,
                     char *keyword_value, unsigned long keyword_position,
                     unsigned short search_scope)

#endif
{
    OBJDESC *start_object = {NULL};
    OBJDESC *obj = {NULL};
    KEYWORD *kwd = {NULL};
    KEYWORD *found_kwd = {NULL};
    unsigned short found = {FALSE};
    unsigned short scope = {search_scope};
    unsigned long current_position = {0};

    if (start_keyword != NULL)
    {
        start_object = start_keyword->parent;
        obj = start_object;
        kwd = start_keyword;
   
        do
        {
            for ( ; ((kwd != NULL) && (! found)); kwd=kwd->right_sibling)
            {
                if (keyword_name == NULL)
                    found = TRUE;
                else
                    found = OdlWildCardCompare(keyword_name, kwd->name);
       
                if ((found) && (keyword_value != NULL))
                    found = OdlWildCardCompare(keyword_value, (char *) OdlGetKwdValue(kwd));
       
                if ((found) && (keyword_position > 0))
                    found = ((++current_position) == keyword_position);
       
                if (found) found_kwd = kwd;   
       
            }  /*  End:  "for (kwd=start_keyword; ..."  */
   
            if (! found)
            {
                obj = (OBJDESC *) OdlNextObjDesc(obj, start_object->level, &scope);
                kwd = (KEYWORD *) OdlGetFirstKwd(obj);
            }
   
        }  while ((obj != NULL) && (! found));

    }  /*  End:  "if (start_keyword != NULL) ..."  */
    else
        odl_errno = 1;

    return(found_kwd);

}  /*  End:  "OdlNextKwd"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlCutKwd                                                       */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine removes a keyword from an object and returns a     */
/*      pointer to it.  All references to the object within the keyword */
/*      are removed, and all references to the keyword within the       */
/*      object are removed.                                             */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO AN EXISTING KEYWORD STRUCTURE   */
/*                AND MUST NOT BE FREED.                                */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlCutKwd (keyword)

KEYWORD *keyword;

#else

KEYWORD *OdlCutKwd (KEYWORD *keyword)

#endif
{
    if (keyword != NULL)
    {
        if (keyword->left_sibling != NULL)
            keyword->left_sibling->right_sibling = keyword->right_sibling;

        if (keyword->right_sibling != NULL)
            keyword->right_sibling->left_sibling = keyword->left_sibling;

        if (keyword->parent->first_keyword == keyword)
            keyword->parent->first_keyword = keyword->right_sibling;

        if (keyword->parent->last_keyword == keyword)
            keyword->parent->last_keyword = keyword->left_sibling;

        keyword->parent = NULL;
        keyword->left_sibling = NULL;
        keyword->right_sibling = NULL;

    }  /*  End:  "if ((keyword != NULL) && ..."  */
    else
        odl_errno = 1;

    return(keyword);

}  /*  End routine:  "OdlCutKwd"  */


/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlPasteKwd                                                     */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine adds a keyword to the end of an object's keyword   */
/*      list.                                                           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlPasteKwd (keyword, object)

KEYWORD *keyword;
OBJDESC *object;

#else

KEYWORD *OdlPasteKwd (KEYWORD *keyword, OBJDESC *object)

#endif
{
    if ((keyword != NULL) && (object != NULL))
    {
        keyword->parent = object;
        keyword->left_sibling = object->last_keyword;
        keyword->right_sibling = NULL;

        if (object->first_keyword == NULL)
            object->first_keyword = keyword;

        if (object->last_keyword != NULL)
            object->last_keyword->right_sibling = keyword;

        object->last_keyword = keyword;

    }  /*  End:  "if ((keyword != NULL) && ..."  */
    else
        odl_errno = 1;

    return(keyword);

}  /*  End routine:  "OdlPasteKwd"  */


/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlPasteKwdBefore
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine adds a keyword to an object as the left sibling of */
/*      the old_keyword.                                                */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlPasteKwdBefore (new_keyword, old_keyword)

KEYWORD *new_keyword;
KEYWORD *old_keyword;

#else

KEYWORD *OdlPasteKwdBefore (KEYWORD *new_keyword, KEYWORD *old_keyword)

#endif
{
    if ((new_keyword != NULL) && (old_keyword != NULL))
    {
        new_keyword->parent = old_keyword->parent;
        new_keyword->left_sibling = old_keyword->left_sibling;
        new_keyword->right_sibling = old_keyword;

        if (old_keyword->left_sibling == NULL)
            old_keyword->parent->first_keyword = new_keyword;
        else
            old_keyword->left_sibling->right_sibling = new_keyword;

        old_keyword->left_sibling = new_keyword;

    }  /*  End:  "if ((new_keyword != NULL) && ..."  */
    else
        odl_errno = 1;

    return(new_keyword);

}  /*  End routine:  "OdlPasteKwdBefore"  */


/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlPasteKwdAfter                                                */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine adds a keyword to an object as the right sibling   */
/*      of the old_keyword.                                             */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlPasteKwdAfter (new_keyword, old_keyword)

KEYWORD *new_keyword;
KEYWORD *old_keyword;

#else

KEYWORD *OdlPasteKwdAfter (KEYWORD *new_keyword, KEYWORD *old_keyword)

#endif
{
    if ((new_keyword != NULL) && (old_keyword != NULL))
    {
        new_keyword->parent = old_keyword->parent;
        new_keyword->right_sibling = old_keyword->right_sibling;
        new_keyword->left_sibling = old_keyword;

        if (old_keyword->right_sibling == NULL)
            old_keyword->parent->last_keyword = new_keyword;
        else
            old_keyword->right_sibling->left_sibling = new_keyword;

        old_keyword->right_sibling = new_keyword;

    }  /*  End:  "if ((new_keyword != NULL) && ..."  */
    else
        odl_errno = 1;

    return(new_keyword);

}  /*  End routine:  "OdlPasteKwdAfter"  */


/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlCopyKwd                                                      */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine makes a copy of a keyword and returns a pointer to */
/*      it.  All of the keyword's fields are duplicated except for      */
/*      references to the parent object, which are removed.             */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlCopyKwd (keyword)

KEYWORD *keyword;

#else

KEYWORD *OdlCopyKwd (KEYWORD *keyword)

#endif
{
    KEYWORD *new_keyword = {NULL};

    if (keyword != NULL)
    {
        new_keyword = OdlNewKwd(keyword->name, keyword->value,
                                 keyword->pre_comment, keyword->line_comment,
                                 keyword->file_name, keyword->line_number);
    }
    else
        odl_errno = 1;

    return(new_keyword);

}  /*  End routine:  "OdlCopyKwd"  */


/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlNewKwd                                                       */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*      12-11-95    sm/LASP - Replaced 'malloc' by 'OaMalloc'.          */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine creates a new keyword structure, initializes       */
/*      its fields with the values passed in, and returns a pointer     */
/*      to it.                                                          */
/*                                                                      */     
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlNewKwd (keyword_name, value_text, pre_comment,
                    line_comment, file_name, line_number)

char *keyword_name;
char *value_text;
char *pre_comment;
char *line_comment;
char *file_name;
long line_number;

#else

KEYWORD *OdlNewKwd (char *keyword_name, char *value_text, char *pre_comment,
                    char *line_comment, char *file_name, long line_number)

#endif
{
    KEYWORD *new_keyword = {NULL};

    if ((new_keyword = (KEYWORD *)OaMalloc((long) sizeof(KEYWORD))) == NULL)
    {
        odl_errno = 50;
        SayGoodbye()
    }
    else
    {
        CopyString(new_keyword->name, keyword_name)
        CopyString(new_keyword->pre_comment, pre_comment)
        CopyString(new_keyword->line_comment, line_comment)
        CopyString(new_keyword->file_name, file_name)
        CopyString(new_keyword->value, value_text)

        new_keyword->is_a_pointer = (keyword_name == NULL) ? FALSE : (*keyword_name == '^');

        if (value_text == NULL)
        {
            new_keyword->size = 0;
            new_keyword->is_a_list = FALSE;
        }
        else
        {
            new_keyword->size = strlen(new_keyword->value);
            new_keyword->is_a_list = ((*value_text == '{') || (*value_text == '('));
        }

        new_keyword->line_number = line_number;
        new_keyword->parent = NULL;
        new_keyword->left_sibling = NULL;
        new_keyword->right_sibling = NULL;
        new_keyword->appl1 = NULL;
        new_keyword->appl2 = NULL;

    }  /*  End:  "if ((new_keyword = ... else ..."  */

    return(new_keyword);

}  /*  End routine:  "OdlNewKwd"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetFirstKwd                                                  */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns a pointer to the first keyword data        */
/*      structure in an object definition structure.                    */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO THE ACTUAL INFORMATION STORED   */
/*                IN THE OBJECT DATA STRUCTURE AND MUST NOT BE FREED.   */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlGetFirstKwd (object)

OBJDESC *object;

#else

KEYWORD *OdlGetFirstKwd (OBJDESC *object)

#endif
{
    KEYWORD *kwd = {NULL};

    if (object != NULL)
        kwd = object->first_keyword;
    else
        odl_errno = 1;

    return(kwd);

}  /*  End:  "OdlGetFirstKwd"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetNextKwd                                                   */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns a pointer to the next keyword data         */
/*      structure in an object definition's list of keyword structures. */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO THE ACTUAL INFORMATION STORED   */
/*                IN THE OBJECT DATA STRUCTURE AND MUST NOT BE FREED.   */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

KEYWORD *OdlGetNextKwd (keyword)

KEYWORD *keyword;

#else

KEYWORD *OdlGetNextKwd (KEYWORD *keyword)

#endif
{
    KEYWORD *kwd = {NULL};

    if (keyword != NULL)
        kwd = keyword->right_sibling;
    else
        odl_errno = 1;

    return(kwd);

}  /*  End:  "OdlGetNextKwd"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetKwdValue                                                  */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine returns a pointer to a keyword's value.            */
/*                                                                      */
/*      WARNING:  NO MEMORY IS ALLOCATED BY THIS ROUTINE.  THE RETURN   */
/*                VALUE IS A POINTER TO THE ACTUAL INFORMATION STORED   */
/*                IN THE KEYWORD DATA STRUCTURE AND MUST NOT BE FREED.  */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

char *OdlGetKwdValue (keyword)

KEYWORD *keyword;

#else

char *OdlGetKwdValue (KEYWORD *keyword)

#endif
{
    char *value = {NULL};

    if (keyword != NULL)
        value = keyword->value;
    else
        odl_errno = 1;

    return(value);

}  /*  End:  "OdlGetKwdValue"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetAllKwdValues                                              */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine extracts each individual value from a set or       */
/*      sequence of values of a keyword, stores these values in a       */
/*      linked list, and returns a pointer to this list.                */
/*                                                                      */
/*      For example, if a keyword has this combination of sets and      */
/*      sequences as its value:                                         */
/*                                                                      */
/*         {red, (green, blue), {17, (("book.lbl", 345), orange)}}      */
/*                                                                      */
/*      Then the TB_STRING_LIST returned would contain:                 */
/*                                                                      */
/*         red                                                          */
/*         green                                                        */
/*         blue                                                         */
/*         17                                                           */
/*         "book.lbl"                                                   */
/*         345                                                          */
/*         orange                                                       */
/*                                                                      */
/*      WARNING:  The string list must be freed using the               */
/*                RemoveStringList macro (look in toolbox.h).           */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

TB_STRING_LIST *OdlGetAllKwdValues (keyword)

KEYWORD *keyword;

#else

TB_STRING_LIST *OdlGetAllKwdValues (KEYWORD *keyword)

#endif
{
    TB_STRING_LIST *value_list = {NULL};
    char *val_start = {NULL};
    char *val_stop = {NULL};
    char save_ch;

    if (keyword != NULL)
    {
        if (keyword->value != NULL)
        {
            for (val_start=(char *)OdlValueStart(keyword->value);
                     *val_start != '\0';
                         val_start=(char *)OdlValueStart(val_stop+1))
            {
                val_stop = (char *) OdlValueEnd(val_start);
                save_ch = *(val_stop + 1); *(val_stop + 1) = '\0';
                AddStringToList(val_start, value_list)
                *(val_stop + 1) = save_ch;
            }

        }  /*  End:  "if (keyword->value != NULL) ..."  */

    }  /*  End:  "if (keyword != NULL) ..."  */
    else
        odl_errno = 1;

    return(value_list);

}  /*  End:  "OdlGetAllKwdValues"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetKwdValueType                                              */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine determines the data type of a keyword's value and  */
/*      returns a data type symbolic id.  Possible symbolic ids are:    */
/*                                                                      */
/*         ODL_UNKNOWN   (can't tell what the heck it is)               */
/*         ODL_INTEGER   (handles optional leading plus or minus)       */
/*         ODL_REAL      (handles optional leading plus or minus,       */
/*                        scientific notation, and real exponents)      */
/*         ODL_SYMBOL    (unqouted or single quoted string of           */
/*                        characters)                                   */
/*         ODL_TEXT      (double quoted string of characters)           */
/*         ODL_DATE      (yyyy-mm-dd or yyyy-ddd)                       */
/*         ODL_DATE_TIME (yyyy-mm-ddThh:mm:ss.h)                        */
/*         ODL_SEQUENCE  (starts with a paren character "(")            */
/*         ODL_SET       (starts with a brace character "{")            */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

unsigned short OdlGetKwdValueType (keyword)

KEYWORD *keyword;

#else

unsigned short OdlGetKwdValueType (KEYWORD *keyword)

#endif
{
    unsigned short type = {ODL_UNKNOWN};
    if (keyword != NULL)
         type = (unsigned short) OdlDataType(keyword->value);
    else
         odl_errno = 1;

    return(type);

}  /*  End:  "OdlGetKwdValueType"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetKwdUnit                                                   */
/*                                                                      */
/*  Author:                                                             */
/*                                                                      */
/*      David P. Bernath (Jet Propulsion Laboratory)                    */
/*                                                                      */
/*  Version:                                                            */
/*                                                                      */
/*      1.0    March 31, 1994                                           */
/*                                                                      */
/*  Change History:                                                     */
/*                                                                      */
/*      03-31-94    Original code                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/
/*                                                                      */
/*  Description:                                                        */
/*                                                                      */
/*      This routine locates the units part of a keyword's value,       */
/*      extracts it, stores it in a new character string, and returns   */
/*      a pointer to this new character string.                         */
/*                                                                      */
/*      WARNING:  This routine allocates memory for the return value    */
/*                that must be freed.                                   */
/*                                                                      */
/************************************************************************/

#ifdef _NO_PROTO

char *OdlGetKwdUnit (keyword)

KEYWORD *keyword;

#else

char *OdlGetKwdUnit (KEYWORD *keyword)

#endif
{
    char *c = {NULL};
    char *unit = {NULL};

    /*  If we were given a keyword to use  */
    if (keyword != NULL)
    {
        /*  Attempt to locate the units string  */
        c = (char *) strchr(keyword->value, '<');

        if (c != NULL)
        {
            /*  We found it!  Now copy it and make it upper case  */
            CopyString(unit, c)
            UpperCase(unit)
   
            /*  Close off the units string  */
            c = (char *) strchr(unit, '>');
            if (c != NULL) *(c + 1) = '\0';
        }

    }  /*  End:  "if (keyword != NULL) ..."  */
    else
        odl_errno = 1;

    return(unit);

}  /*  End:  "OdlGetKwdUnit"  */



/************************************************************************/
/*                                                                      */
/*  Component:                                                          */
/*                                                                      */
/*      OdlGetKwdName                           