PIRL Parameter Value Logic PIRL Implementation of the Parameter Value Language interpreter. Description: * Parameter Value Language The Parameter Value Language is commonly used in the label section of image files found on Planetary Data System CDs as well as other data products from the Planeteary Science and Astronomical communities. The language specification is provided by the Consultative Committee for Space Data Systems in the Blue Book ("Parameter Value Language Specification (CCSDS0006)", May 1992 [CCSDS 640.1-B-1]) and Green Book ("Parameter Value Language - A Tutorial", May 1992 [CCSDS 640.1-G-1]) documents. The language has a deceptively simple syntax for a parameter defining PVL statement: name = value A parameter is just a token name (i.e. a string) that is used to refer to the corresponding value. The optional value is a single datum representing a number (integer or real) or string, or an array of zero or more of these values. The optional units string which may follow any value, including an array, is a string enclosed in angle braces. Without getting into the details (which are in the references), there are complexities to the value representation that make managing the resultant data structures somewhat problematical. The parameter value statement is not required to be on a single line (a string terminated by a line-feed and/or carriage-return character). Comments (C-language style) may be provided on the same or separate lines. An integer may be represented with a specified-base notation in addition to the usual decimal notation. A string may be unquoted, single quoted, or double quoted and may represent a special date/time notation. Quoted strings may extanded across multiple lines with the line separators, any optional preceding hyphen, and any following white space being replaced with a single space character. Arrays may be unordered sets contained in curly braces or ordered sequences contained in parentheses, with a possible mix of value types. * PIRL Parameter Value Logic It is an unfortunate fact of life that the Parameter Value Language has not always been used with strict adherence to the syntax rules. Therefore, this implementation of the language parser is designed to be very tolerant, rather than requiring strict adherance to a rigid specification; i.e. the intent is to "get the information" if possible, and let the user decide what to do with it. For example: Any string may be quoted (with single or double quotation marks), including the parameter name. Sets and sequences are treated identically as arrays. A list of values that is not enclosed in curly braces or parentheses is treated as a set. Arrays may be nested to any depth. Base notation (N#MMM#) allows any base value that can be handled by strtol. Date and time values are treated as strings (they are not interpreted). Comments may be anywhere in a statement (except inside quoted strings where they are treated as part of the string), including crossing record/line boundaries. Comments that lack termination will be assumed to end at the next line break. Unterminated units strings will be assumed to end at the next parameter value separator. The PPVL_strict global variable controls whether the PVL will be interpreted strictly or with tolerance. By default this module is compiled with PPVL_strict initialized to FALSE (i.e. use tolerance). Whenever strict PVL interpretation would produce an ERROR condition, a WARNING is registered (see Error Handling, below, for more details). Parameters - The parser stores the information it gathers in a PIRL Parameter Value Logic Parameter structure: typedef int PPVL_Class; typedef struct PPVL_parameter { PPVL_Class classification; Classification of parameter char *name; Parameter name (keyword) union { For all classes of parameters EXCEPT begin aggregate: struct PPVL_value *value; ONLY for begin aggregate class parameters: struct PPVL_parameter **parameters; } content; Parameter content (value) char *comments; Leading comments void *user_data; Link to user-defined data } PPVL_Parameter; Each parameter is of a particluar classification: Unknown (PPVL_CLASS_UNKNOWN) - this should only occur in the case of a parsing error. Token (PPVL_CLASS_TOKEN) - a parameter with no values (this does not include parameters with empty sets or sequences). End (PPVL_CLASS_END) - a token with the special name "END". Assignment (PPVL_CLASS_ASSIGNMENT) - the normal parameter value assignment. Aggregate (PPVL_CLASS_AGGREGATE or PPVL_CLASS_BEGIN_AGGREGATE) - One of the special classifications indicated by a parameter with a special (reserved) name: OBJECT or BEGIN_OBJECT (PPVL_CLASS_OBJECT or PPVL_CLASS_BEGIN_OBJECT) END_OBJECT (PPVL_CLASS_END_OBJECT) GROUP or BEGIN_GROUP (PPVL_CLASS_GROUP or PPVL_CLASS_BEGIN_GROUP) END_GROUP (PPVL_CLASS_END_GROUP Both Objects and Groups are in the Aggregate classification. Special names are not case sensitive. Aggregate classification parameters (both begin and end) may optionally be assigned a value - which is expected to be a string which names the aggregate - that is used as the parameter name when available. The distinction between an Object and a Group may be meaningful to the user (e.g. an Object may include parameters that collectively form a larger object, while the parameters of a Group may only have some common quality), but they are otherwise identical. Note: End-class parameters are not retained in parameter lists (i.e. aggregate values). They are processed on input (PPVL_scan_parameter) and output (PPVL_write_parameter) only. Except for specialized application purposes, they are not needed. The classification code of a parameter is actually a collection of bit flags. Only specific aggregate classification codes (group, object), as opposed to general codes (aggregate) can be meaningfully used directly. A set of PPVL_Class_Is_ macros are provided for ease in evaluating classification codes. For Class substitute any (except Unknown) of the classification names (as in PPVL_CLASS_) in initial caps form. For example: if (PPVL_Class_Is_Assignment (The_Parameter->classification)) { Process assignment parameter ... } else if (PPVL_Class_Is_Aggregate (The_Parameter->classification)) { if (PPVL_Class_Is_Group (The_Parameter->classification)) { Group specific processing ... } The parameter's name symbolically identifies the parameter. When the parameter is in the aggregate classification, the name is reset to the identifier string value, if it has one (otherwise the name is left as the special aggregate name). Parameter names (or aggregate identifiers) need not be unique within the aggregate in which they occur (but then it is left to the user to distinguish them). Any comments that occur immediately preceding a PVL statement are collected together in the parameter's "comments" string. A comment is identified as a string enclosed in C-language style comment characters. Multiple comments are delimited by a single new-line ('\n') character in the "comments" string. Comments that are embedded within a parameter statement are ignored. A "user" pointer is provided for the user to link any desired data to a parameter. This pointer is not used in this module in any way (other than as a potential selection to PPVL_find_parameter). Values - The "content" element is either a "value" - a PPVL_Value pointer - or, for aggregate parameters ONLY, a list of "parameters" - a pointer to an array of PPVL_Parameter pointers (see the Aggregates section below). A PPVL_Value contains the value portion of the PVL statement: typedef int PPVL_Type; typedef struct PPVL_value { PPVL_Type type; Data type union { unsigned long integer; double real; char *string; struct PPVL_value **array; } data; Actual data value int base; Number system base (radix) char *units; Measurement units } PPVL_Value; The data for each value is either an integer number (long precision), a real number (double precision), a string pointer, or itself an array of value structures. In this latter case, data.array points to a NULL terminated list of PPVL_Value pointers. The data type of each value is indicated by a type code: PPVL_TYPE_UNKNOWN - only occur in the case of a parsing error. PPVL_TYPE_NUMERIC - includes these specific types: PPVL_TYPE_INTEGER (unsigned long) PPVL_TYPE_REAL (double) PPVL_TYPE_STRING - includes these specific types: PPVL_TYPE_IDENTIFIER - an unquoted string. PPVL_TYPE_SYMBOL - single quoted (') string. PPVL_TYPE_TEXT - double quoted (") string. PPVL_TYPE_DATE_TIME - a guess (contains "-" and/or ":"). PPVL_TYPE_ARRAY - includes these specific types: PPVL_TYPE_SET - contained within curly braces, or an uncontained values list. PPVL_TYPE_SEQUENCE - contained within parentheses. The type code of a parameter is actually a collection of bit flags. Only specific type codes (e.g. integer or text), as opposed to general codes (e.g. numeric or string) can be meaningfully used directly. A set of PPVL_Type_Is_ macros are provided for ease in evaluating type codes. For Type substitute any (except Unknown) of the type names (as in PPVL_TYPE_) in initial caps form. For example: if (PPVL_Type_Is_String (The_Value->type)) { Process string value ... } else if (PPVL_Type_Is_Numeric (The_Value->type)) { if (PPVL_Type_Is_Real (The_Value->type)) { Real value processing ... } Each value may have a units string. The string is typically used to provide the units of measurement for the corresponding value, but its interpretation is left to the user. An array (set or sequence) value may have a units string in addition to a possible units string for each value of the array. The units string of an array value may be taken as being applied to each value of the array unless the array value has its own units string, however, this is just a suggested use. Aggregates - When a parameter is of the begin aggregate classification (OBJECT or GROUP) its content.value is a pointer to a NULL terminated list of PPVL_Parameter pointers (PPVL_Parameter **). Each parameter in the list is a member of the aggregate parameter. The PVL statement that ends the list of aggregate parameter members is not in this list (the NULL implicitly represents the end-aggregate parameter); it is swallowed on input (PPVL_read_aggregate), and recreated on output (PPVL_write_parameter). The previous value of the aggregate parameter (as returned from PPVL_scan_parameter), if any, is assigned to the parameter's name. Aggregate parameters without a single string value retain their original name. Inappropriate values (not a single string) will generate a warning (or error in strict mode). * Dynamic memory The PPVL module uses dynamically allocated memory throughout. All PPVL objects (parameter structures and aggregate lists, value structures and array lists) and any string contents are stored in dynamic memory. Some (actually not much) effort has been made to be efficient in the use of the system's dynamic memory functions. It is unnecessary, and potentially unsafe, to use static or stack memory for PPVL objects. Parameter and value structures, once created (directly or indirectly), will remain stable until freed (only use the PPVL_free functions), but parameter and value lists will may be reallocated whenever a PPVL function feels the need. The use of dynamic memory is not expected to produce any hardship for the application as long as the PPVL functions are used exclusively to manipulate its objects. Use: #include "PPVL.h" PPVL_Parameter * PPVL_read_aggregate ( FILE *File, unsigned long Read_Limit, unsigned long *Total_Scanned ); PPVL_Parameter * PPVL_scan_aggregate ( char *The_String, char **Next_Character ); PPVL_Parameter * PPVL_scan_parameter ( char *The_String, char **Next_Character ); char * PPVL_scan_comments ( char *The_String, char **Next_Character ); PPVL_Value * PPVL_scan_value ( char *The_String, char **Next_Character ); PPVL_Error_Code PPVL_scan_datum ( PPVL_Value *The_Value, char *The_String, char **Next_Character ); char * PPVL_scan_quoted_string ( char *The_String, char **Next_Character ); char * PPVL_scan_units ( char *The_String, char **Next_Character ); char * PPVL_skip_white_space_and_comments ( char *The_String ); char * PPVL_comb_symbol ( char *The_String ); PPVL_Parameter * PPVL_new_parameter ( char *name, PPVL_Class classification, PPVL_Object *content, char *comments, void *user_data ); PPVL_Value * PPVL_new_value ( PPVL_Type type, PPVL_Object *data, int base, char *units ); PPVL_Parameter * PPVL_add_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *The_Parameter ); PPVL_Value * PPVL_add_value ( PPVL_Value *The_Array, PPVL_Value *The_Value ); PPVL_Parameter * PPVL_duplicate_parameter ( PPVL_Parameter *The_Parameter ); PPVL_Value * PPVL_duplicate_value ( PPVL_Value *The_Value ); PPVL_Parameter * PPVL_remove_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *The_Parameter ); PPVL_Value * PPVL_remove_value ( PPVL_Value *The_Array, PPVL_Value *The_Value ); void PPVL_free_parameter ( PPVL_Parameter *The_Parameter ); void PPVL_free_value ( PPVL_Value *The_Value ); PPVL_Parameter * PPVL_find_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *Last_Parameter, PPVL_Select Select, PPVL_Object *The_Selection, PPVL_Parameter **The_Parent ); PPVL_Value * PPVL_find_value ( PPVL_Value *The_Array, PPVL_Value *Last_Value, PPVL_Type Type, PPVL_Object *Data, PPVL_Value **The_Parent ); int PPVL_count_all_parameters ( PPVL_Parameter *The_Aggregate ); int PPVL_count_all_values ( PPVL_Value *The_Array ); int PPVL_count_parameters ( PPVL_Parameter *The_Aggregate ); int PPVL_count_values ( PPVL_Value *The_Array ); int PPVL_index_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *The_Parameter ); int PPVL_index_value ( PPVL_Value *The_Array, PPVL_Value *The_Value ); PPVL_Error_Code PPVL_write_parameter ( PPVL_Parameter *The_Parameter, FILE *The_File, int Indent_Level ); PPVL_Error_Code PPVL_write_value ( PPVL_Value *The_Value, FILE *The_File ); A pseudo-object-oriented interface is also provided. These functions (actually macros to the specific functions above) operate on PPVL_Object handles, which may be PPVL_Parameter or PPVL_Value pointers: PPVL_Object * PPVL_new ( int Type, Class or type code PPVL_Object *Content, char *Name, Parameter name or data radix char *Comments, Parameter comments or value units void *User_Data ); PPVL_Object * PPVL_add ( PPVL_Object *The_Container, PPVL_Object *The_Object ); PPVL_Object * PPVL_duplicate ( PPVL_Object *The_Object ); PPVL_Object * PPVL_remove ( PPVL_Object *The_Container, PPVL_Object *The_Object ); void PPVL_free ( PPVL_Object *The_Object ); PPVL_Object * PPVL_find ( PPVL_Object *The_Container, PPVL_Object *Last_Object, int Select, PPVL_Select or PPVL_Type PPVL_Object *The_Selection, Parameter selection or value data PPVL_Object *The_Parent ); int PPVL_count_all ( PPVL_Object *The_Container ); int PPVL_count ( PPVL_Object *The_Container ); int PPVL_index ( PPVL_Object *The_Container, PPVL_Object *The_Object ); The PPVL functions are organized in such a manner that applications usually only need to call a very few functions. This becomes clearer when the relationship amongst the functions is understood: * Input Functions PPVL_read_aggregate PPVL_scan_aggregate PPVL_scan_parameter PPVL_scan_comments PPVL_scan_value PPVL_scan_datum PPVL_scan_quoted_string PPVL_scan_units PPVL_skip_white_space_and_comments PPVL_comb_symbol These functions manage the conversion of external PVL statments to internal PPVL objects. Usually only the PPVL_read_aggregate function is needed, though the PPVL_scan_aggregate function is used for special circumstances where the application itself reads the PVL text into a buffer for processing. In general, the lower level functions are not needed by the application. They are made available for applications that need to handle the PVL input process in some unique, specialized manner. PPVL_read_aggregate - An aggregate parameter (PPVL_Parameter), given the name "The Container" (but use the symbol PPVL_CONTAINER_NAME), is assembled from all of the PVL parameters that can be read from the specified File. The total number of characters scanned to produce the aggregate parameter is returned through Total_Scanned (if not NULL). Thus Total_Scanned will provide the offset from the position of the file when the function was called to the location of the byte that ended the last PVL parameter statement scan (i.e. the next byte after those that were used by the function). If the function fails to produce an aggregate parameter, then Total_Scanned returns the location of the character that caused the failure. If the file is seekable (a disk file), then it will be repositioned to the location specified by Total_Scanned before the function returns (regardless of whether the function succeeds or not, or if Total_Scanned is NULL or not). The specified file reference (i.e. a stream opened for input) is used to read characters starting at the file's current position into a dynamically sized sliding window buffer. This buffer is used as the source of The_String to repeatedly pass to the PPVL_scan_parameter function. An internal function is recursively called to assemble any aggregate parameters that are encountered during the file scan. The scan of the file contents ends when any one of these conditions occurs (only the first is strictly legal): An END (PPVL_CLASS_END) parameter is encountered. The Read_Limit (measured in number of bytes) is reached. The end of the file is reached. No parameter can be formed due to uninterpretable syntax or an empty PVL statement. Sized (a.k.a. variable) record formatted files, in which each record is preceeded with a binary count value and possibly padded with a null byte, are handled correctly. Because some files do not provide a PVL END statement (or any other mark for the end of the PVL statements list!), it is possible for file scanning to proceed into non-PVL file data. This can be controlled by a user-provided Read_Limit (see the Examples section below), but a default limit (256 kb) will be used if unlimited file scanning is specified (Read_Limit set to PPVL_NO_READ_LIMIT). It is possible that one or more bogus parameters can thus be appended to the list returned, however this is usually not a problem as they will probably be distinctly outrageous to the application. On success a pointer to The_Container aggregate parameter is returned. On failure a NULL is returned. A failure occurs if, and only if, a fatal error occurs during the file scan (see the Error handling section for a list of fatal errors). When sized records are encountered the warning status PPVL_ERROR_SIZED_RECORDS is set in PPVL_errno. The "scan" functions scan a character string searching for and collecting their appropriate PVL element. Each function takes an argument called The_String which indicates the starting location of the string to be scanned. And each function will return through the Next_Character argument (if it is not NULL) where the scan left off. The_String is never modified in any way. When the function successfully acquires its PVL element the Next_Character will be the beginning of the next PVL element (or the end of the string). This allows the user to repeatedly call the function to obtain all contiguous PVL elements, setting The_String to the value returned through Next_Character after each call. When no corresponding PVL element is available from The_String the function will return a failure condition and the location in The_String where the scan failed will be returned through the Next_Character argument. Note that for some PVL elements, only one element is expected from The_String (e.g. the datum or units for a value), but the Next_Character still correctly indicates where the scan left off for use by the next PPVL function. PPVL_scan_aggregate - Just like PPVL_read_aggregate, but operating on a string instead of a file, this function returns an aggregate named "The Container" that has a list of all parameters found in the string. On success a pointer to The_Container aggregate parameter is returned. On failure a NULL is returned. A failure occurs if, and only if, a fatal error occurs during The_String scan. PPVL_scan_parameter - A parameter (PPVL_Parameter) is assembled from the contents of The_String. The next character after the the PVL statement is returned through Next_Character (if it is not NULL) on success; the location in The_String where the error occured otherwise. After collecting the leading comments and the parameter name, if an equals sign character is present the remainder of The_String is passed to PPVL_scan_value to abtain the value of the parameter. When the parameter name is not a quoted string it is checked for reserved characters (from the PVL specification) and a warning (or error in strict mode) is generated if any are found. When the parameter's name is one of the special names - OBJECT, BEGIN_OBJECT, END_OBJECT, GROUP, BEGIN_GROUP, or END_GROUP - it becomes one of the corresponding aggregate classifications. In this case when the value obtained, if any, is a single string type this string is reassigned as the parameter name, and the remainder of the PPVL_value is freed. When there is no string value, or the value is inappropriate, the parameter retains its special name (and a warning is generated). On success a pointer to the new parameter is returned. On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_String is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory for the new parameter structure, or any of its components, could not be allocated. PPVL_ERROR_EMPTY_STATEMENT The_String is empty (nothing to parse). PPVL_ERROR_ILLEGAL_SYNTAX The parameter name is quoted (strict mode only). PPVL_ERROR_RESERVED_CHARACTER The unquoted parameter name contains a reserved character or unprintable character. PPVL_ERROR_GROUP_VALUE The parameter is an aggregate with a value that is not a single string. PPVL_scan_value - A value (PPVL_Value) is assembled from the contents of The_String. The next character after the the PVL statement is returned through Next_Character (if it is not NULL) on success; the location in The_String where the error occured otherwise. It is initially assumed that the value being assembled will be an array, so a list of PPVL_Value pointers is built and attached to a new array value. The_String is scanned for potential datum symbols, which are passed as they are encountered to the PPVL_scan_datum function along with an empty PPVL_Value structure. If, however, the beginning of an array enclosure (the '(' and ')' characters enclose sequences; the '{' and '}' characters enclose sets) is encountered, then the PPVL_scan_value function is recursively called to assemble the array value. After each datum has been scanned (whether an array or not) the PPVL_scan_units function is passed the remainder of the The_String in an attempt to obtain a units of measurement string for the value. If a syntax error results it is ignored and the scan for datum symbols continues where it left off. When there are no more datum symbols, or an end of array enclosure character, end of PVL statement character (';'), or the end of string is encountered the scan ends. When only one value has been found and it is not inside an array enclosure, the initial array value and its value list are freed and the single value found is returned instead. On success a pointer to the new value structure is returned. On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_String is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory for the new value structure, or any of its components, could not be allocated. PPVL_ERROR_ILLEGAL_SYNTAX A parameter name delimiter ('='), value delimiter (','), units start or end delimiter ('<', '>'), or number base (radix) delimiter ('#') was encountered when a value datum was expected. These cases are treated as non-recoverable since they are so egregious. A set or sequence array opening ('{', '(') was encountered when a datum was expected. PPVL_ERROR_ARRAY_CLOSURE_MISMATCH The matching set or sequence array closure ('}', ')') is missing. PPVL_scan_datum - The_String is expected to contain a single datum symbol to be converted and assigned as the appropriate data of The_Value. A datum symbol with enclosing single or double quotation marks is duplicated (without the enclosing marks) as the data.string of The_Value and the data type code is set accordingly. For unadorned symbols an attempt is first made to convert the symbol as an integer (unsigned long) numeric value. An integer may be represented in decimal, hexadecimal (with a leading "0X" or "0x"), or octal (with a leading '0') form. If the entire symbol is not converted and it failed on the special base notation symbol then an attempt is made to convert it assuming base notation: [Sign]Base#Radix# Where: Sign is either '+' or '-'. Base is an integer (2-36) that is the base of the value. Radix is an integer value with the specified Base. For example: 2#101101# is base 2 (binary) for decimal 45. 16#2D# is base 16 (hexadecimal) for the same decimal 45. When an integer value can not be converted an attempt is made to convert the symbol into a real (double) value. If the entire symbol is not converted in this way it is taken to be an unquoted string. The latter is checked for characters that might indicate that it is a date/time string, and if so the data type code is set accordingly but no attempt is made to interpret this string. Unquoted string symbols are also checked for the presence of reserved characters. This function returns a PPVL_Error_Code. It will be zero (PPVL_SUCCESS) on success; non-zero otherwise. The character immediately following the datum symbol is returned through Next_Character (if non-NULL) on success; the location in The_String where the error occured otherwise. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Value or The_String is NULL. PPVL_ERROR_EMPTY_STATEMENT No datum symbol was found. A datum symbol is missing. These are non-recoverable errors. PPVL_ERROR_ILLEGAL_SYNTAX A parameter or value delimiter; a set, sequence, or units open or close delimiter; or a number base delimiter was found at the beginning of a datum symbol. Instead of tolerantly taking these to be the beginning of a string they are treated as non-recoverable errors. PPVL_ERROR_VALUE_OVERFLOW Numeric conversion of the datum string produced an overflow condition (the value will not fit in the available precision). A base value is out of range (2-36). PPVL_ERROR_RESERVED_CHARACTER An uquoted string value contains a reserved character. The presence of an unprintable character always produces a non-recoverable error. PPVL_scan_quoted_string - The first character of The_String is used as the enclosing quotation mark (this is usually a single or double quote character, but it is the caller's responsibility to decide what constitutes a quote mark). Line delimiters and any following white space (leading white space on the next line) are replaced by a single space character. If, however, the last character before the line delimiters is a dash ('-') then the line delimiters and following white space are simply compressed out entirely. Normally string formatting for appearance when printed is controlled by embedded format characters which are processed on output: \n - line break. \t - horizontal tab. \f - form feed (page break). \\ - backslash character. \v - verbatim (no formatting) till next \v. The quotation mark may also be included in the quoted string by escaping its normal meaning with a preceeding backslash character. Because some applications format a string value to appear as written (without the use of embedded format characters), the global variable PPVL_verbatim_strings is provided to override the normal hypen and leading white space elimination when its value is set to TRUE. On success a pointer to a copy (in dynamically allocated memory) of the string (without the enclosing quote characters) is returned. On failure a NULL is returned. If The_String is empty (no characters at all) NULL will be returned but PPVL_errno will indicate PPVL_ERORR_NO_ERROR (0). The character immediately following the next occurance of the quote character is returned through Next_Character (if non-NULL) on success; the location in The_String where the error occured otherwise. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_String is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory could not be allocated for a copy of the string. PPVL_ERROR_MISSING_QUOTE_END The terminating quote character can not be found. This is a non-recoverable error. PPVL_scan_units - The_String is scanned for a units of measure string. This string must be enclosed in angle brackets ('<' and '>'). Leading and trailing white space and comments inside this enclosure are ignored and all other white space sequences are reduced to a single space character. On success a pointer to a copy (in dynamically allocated memory) of the units string is returned. On failure a NULL is returned. Note that the inability to find a units string enclosure is a syntax error (PPVL_errno is set to PPVL_ERROR_ILLEGAL_SYNTAX). However, this is likely to be an expected outcome when the function is just used in an attempt to see if a units string is present. The character immediately following the units string enclosure is returned through Next_Character (if non-NULL) on success; the location in The_String where the error occured otherwise. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_String is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory could not be allocated for a copy of the units string. PPVL_ERROR_ILLEGAL_SYNTAX No units start delimiter was found. This always a warning. PPVL_ERROR_MISSING_UNITS_END No units end delimiter was found. This is only an error in strict mode, otherwise the units string is taken to be the single word after the units start delimiter. PPVL_scan_comments - The_String is scanned for C-language style comments. A comment string starts at the character after a forward slash ('/') that is immediately followed by an asterisk ('*'), and ends just before an asterisk that is immediately followed by a forward slash. Sequential comments, with nothing but white space intervening, are accumulated with a single new-line ('\n') chararacter separating them in the resulting string that is returned. In strict mode comments are not allowed to wrap across line breaks. Tolerant interpretation allows this but replaces sequences of line delimiter characters with a single new-line character. Tolerant mode also allows comments to be implicitly terminated at the next line break if normal termination can not be found. On success a pointer to a copy (in dynamically allocated memory) of the comment string is returned. If no comments are found before a non-comment sequence is encounterd, the function returns NULL to indicate a failure, but PPVL_errno is set to PPVL_SUCCESS (0). A failure for any other reason will set PPVL_errno to a non-zero error code. The location of the next non-comment string (after any white space) is returned through Next_Character (if non-NULL) on success; the location in The_String where the error occured otherwise. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_String is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory could not be allocated for a copy of the comments string. PPVL_ERROR_MISSING_COMMENT_END The comment termination sequence was not found. This is an error in strict mode, otherwise the comment is taken to end at the next line break. PPVL_skip_white_space_and_comments - The_String is scanned for contiguous white space characters and comment strings in search of the next occurance of a character that is not a either type. White space characters are the space (' ') and horizontal tab ('\t') characters plus the line delimiter characters - carriage return ('\r'), new line ('\n'), formfeed ('\f'), and vertical tab ('\v'; note that this is a single character not to be confused with the verbatim string formatting character pair). Comment strings are identified in the same manner as in PPVL_scan_comments. On success a pointer to the next character after any white space or comments is returned (note that this may be a pointer to the end of The_String). On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_String is NULL. PPVL_ERROR_MISSING_COMMENT_END The comment termination sequence was not found. This is an error in strict mode, otherwise the comment is taken to end at the next line break. PPVL_comb_symbol - The_String is checked for a reserved character or a non-printable character. The reserved characters are: "{}()[]<>&\"',=;#%~|+! \t\r\n\f\v" A pointer to the first illegal character found is returned, or NULL if none is found. This function can not produce any error/warning conditions and does not set PPVL_errno to PPVL_SUCCESS. * Object manipulation functions PPVL_new_parameter PPVL_new_value PPVL_add_parameter PPVL_add_value PPVL_duplicate_parameter PPVL_duplicate_value PPVL_remove_parameter PPVL_remove_value PPVL_free_parameter PPVL_free_value These functions are used to manage the complete life cycle of PPVL_Parameter and PPVL_Value objects. PPVL_new_parameter - A new PPVL_Parameter structure is created in dynamic memory with contents derived from the function arguments. Cross checks for consistency are made and parameter elements that are not specified are filled in, when possible, from implicit information in other arguments. The "name" argument is the copied as the name element of the parameter. When the name is one of the special names indicating an aggregate parameter, then the parameter classification is set accordingly. Normally the name for an aggregate is its identifier string and the classification of the parameter is determined from the "classification" argument or derived from the "content" argument. The "classification" argument, when not zero, must be for a specific classification (e.g. PPVL_CLASS_GROUP), not a general classification (e.g. PPVL_CLASS_AGGREGATE). The "content" argument is a pointer to a PPVL_Parameter or PPVL_Value, or NULL. The "content" is not duplicated, and sharing a parameter or value already associated with another parameter or value, while possible, runs the risk of being "lost" when removed or freed from one context and thus becoming invalid in the other context. It is recommended that "content" be either new or duplicated objects. Comments are copied into dynamic memory. Embedded new-line characters ('\n') will be interpreted on output as separate sequential comment lines preceeding the parameter statement. The "user_data" is anything the user wants to attach to the parameter. Not that it is user's responsibility to manage the user_data. It is possible to create a new empty parameter, but this rarely is useful. On success a pointer to the new parameter structure is returned. On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT A "classification" argument is specified that is not a recognized parameter classification. This is a warning if comments or user_data is specified but no classification can be determined. PPVL_ERROR_NO_MEMORY Dynamic memory could not be allocated for the parameter structure or any of its elements. PPVL_ERROR_ILLEGAL_SYNTAX The "classification" (explicit or implicit) conflicts with the "content". PPVL_new_value - Like PPVL_new_parameter, a new PPVL_Value structure is created in dynamic memory with contents provided by the function arguments. It is possible to create an empty value structure, but this is rarely useful. The "type" must be a specific type (e.g. PPVL_TYPE_INTEGER), not a general type (e.g. PPVL_TYPE_NUMERIC). The interpretation of the "data" argument is based on the "type" argument: PPVL_TYPE_INTEGER - (unsigned long *)data PPVL_TYPE_REAL - (double *)data Any string type - (char *)data Any array type - (PPVL_Value *)data Carefully note the difference in how numeric and other "data" arguments are treated: For numeric data the "data" argument is the address of the variable, not the variable value itself. For other string types the "data" argument is the string pointer variable value (char *), not the address of the string pointer variable. The same applies to array types for which the "data" arguments are "PPVL_Value *", not "PPVL_Value **' (see the Examples section below). Note that value data for array types should not be shared by multiple arrays (see the discussion for PPVL_new_parameter above). The "base" argument is only meaningful for integer data. If the base is not 10 (or zero which is implicitly 10), then radix notation will be used when the value is printed. Note that the base may be negative, in which case the integer value is taken to be negative (this offers one more bit of precision). The "units" argument string is copied into dynamic memory. On success a pointer to the new value structure is returned. On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT A non-zero "data", "base", or "units" argument is specified, but the "type" argument is zero. The "type" argument is not a recognized value type. The "base" argument is non-zero and its absolute value is not in the range 2-36 inclusive. PPVL_ERROR_NO_MEMORY Dynamic memory could not be allocated for the value structure or any of its elements. PPVL_ERROR_ILLEGAL_SYNTAX The "type" is for an array, but the "data" is not a value object. PPVL_add_parameter - The_Parameter object is added to the list of parameters for The_Aggregate parameter. If the The_Aggregate is empty, a new parameter list is created. Note that the parameter list is in dynamic memory and adding a new parameter pointer to the list requires the reallocation of its memory. On success The_Aggregate pointer is returned. This allows this function to be an argument to other parameter manipulation functions such as PPVL_new_parameter or other PPVL_add_parameter calls (see the Examples section below). On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Aggregate or The_Parameter is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory to create or enlarge the parameter list could not be allocated. PPVL_ERROR_ILLEGAL_SYNTAX The_Aggregate is not a pointer to a valid aggregate parameter. The_Parameter is not a pointer to a valid PPVL_Parameter structure (e.g. an empty parameter structure). PPVL_add_value - Just like PPVL_add_parameter, but for PPVL_Value structures. PPVL_duplicate_parameter - A new parameter structure is created in dynamic memory and the contents of The_Parameter are copied into it. If The_Parameter is an aggregate, all of its constituent parameters are duplicated recursively. The user_data element of the parameter is not duplicated, but its value is copied. On success a pointer to the new parameter structure is returned. On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Parameter is NULL. PPVL_ERROR_NO_MEMORY Dynamic memory for the new parameter or any of its consituents could not be allocated. PPVL_ERROR_ILLEGAL_SYNTAX The_Parameter is not a pointer to a valid PPVL_Parameter structure. PPVL_duplicate_value - Just like PPVL_duplicate_parameter, but for PPVL_Value structures. PPVL_remove_parameter - The_Parameter is found in the parameter list of The_Aggregate, or any aggregates in the parameter list, recursively. The parameter list where The_Parameter is found is shortened so as to remove the specified parameter pointer from the list. The dynamic memory for the removed parameter structure and all its contents are freed. On success The_Aggregate pointer is returned. On failure a NULL is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Aggregate or The_Parameter is NULL. PPVL_ERROR_NO_MEMORY The shortened parameter list could not be reallocated. PPVL_ERROR_ILLEGAL_SYNTAX The_Aggregate is not a begin aggregate classification parameter. PPVL_ERROR_EMPTY_STATEMENT The_Parameter could not be found in any parameter list. PPVL_remove_value - Just like PPVL_remove_parameter, but for PPVL_Value structures. PPVL_free_parameter - The_Parameter and all its contents are freed. This includes the parameter name and comments strings. If The_Parameter is an aggregate all the parameters on its list are recursively freed as is the list itself. This function has no return value. It does not modify the PPVL_errno value. PPVL_free_value - Just like PPVL_free_parameter, but for PPVL_Value structures. String data and units strings are freed. For array type values all values in the array are recursively freed along with the arrays value list. * Search Functions PPVL_find_parameter PPVL_find_value These functions are used to search PPVL_Parameter or PPVL_Value objects for selected elements with specified values. PPVL_find_parameter - The parameter list of The_Aggregate is first searched, recursively, for the parameter pointer with the Last_Parameter value. This identifies the starting point for The_Selection search which follows. If Last_Parameter is PPVL_SEARCH_FROM_THE_TOP (NULL), then The_Selection search starts with the first parameter in The_Aggregate list. The search process is depth-wise; i.e. whenever an aggregate is encountered in a parameter list it is searched before the remainder of the parameters in the list. This corresponds to the order in which PVL statements appear. When the The_Selection is found a pointer the parameter that matched is returned. A pointer to the parent aggregate that contains the selected parameter in its list is returned through The_Parent - an address of a parameter pointer (PPVL_Parameter **) - if not NULL. Select indicates how The_Selection is to be interpreted: PPVL_SELECT_ANY (0) The next parameter after Last_Parameter (or the first parameter if Last_Parameter is PPVL_SEARCH_FROM_THE_TOP) is selected. This allows a parameter hierarchy to be stepped through in the order in which the corresponding PVL statements appear externally. This could be done as follows: for (The_Parameter = PPVL_find_parameter ( The_Aggregate, PPVL_SEARCH_FROM_THE_TOP, PPVL_SELECT_ANY, NULL, &The_Parent ); The_Parameter; The_Parameter = PPVL_find_parameter ( The_Aggregate, The_Parameter, PPVL_SELECT_ANY, NULL, &The_Parent )) { Process The_Parameter... } PPVL_SELECT_NAME (1) The_Selection is taken to be a string (char *) that is compared with the name element. The_Selection string may be a simple name which will match with the first occurance of a parameter with the same (but case insensitive) name. It may also be a pathname of the form: [/]Name[/Name[...]] A pathname beginning with a forward slash ('/') is "absolute" in that the first Name must be found in The_Aggregate parameter list before the next Name, delimited by a forward slash, will qualify for a match in a parameter aggregate in this list; etc. This is the same as file pathnames in Unix. A pathname that does not begin with a forward slash is "relative" in that the first Name is searched for like a simple name and then the remainder of the path is searched for relative to the location of the first parameter found. PPVL_SELECT_CLASS (2) The_Selection is taken to be a PPVL_Class code (note that this is the classification code value itself, not a pointer to the value). Both specific and general classification codes are valid. The first occurance of a parameter having the selected classification will match. PPVL_SELECT_PARAMETER (3) The_Selection is taken to be a parameter pointer (PPVL_Parameter *). The parameter with the identical pointer value in a paramter list is selected. This is typically used to traverse back up the parameter heirarchy by searching for the parent parameter returned from a successful search, which returns its parent, until the parent returned is The_Aggregate itself. This could be done as follows: The_Parameter = PPVL_find_parameter ( The_Aggregate, PPVL_SEARCH_FROM_THE_TOP, PPVL_SELECT_NAME, "deeply/nested/parameter", &The_Parent ); for (PPVL_find_parameter ( The_Aggregate, PPVL_SEARCH_FROM_THE_TOP, PPVL_SELECT_PARAMETER, The_Parameter, &The_Parent ); The_Parent != The_Aggregate; PPVL_find_parameter ( The_Aggregate, PPVL_SEARCH_FROM_THE_TOP, PPVL_SELECT_PARAMETER, The_Parent, &The_Parent )) { Process The_Parent... } Process The_Aggregate... PPVL_SELECT_USER_DATA (4) The_Selection is taken to be a void pointer (void *). The first parameter that has user_data with the identical value is selected. On success a pointer to the selected parameter is returned. If The_Parent argument is not NULL a pointer to the parent aggregate that has the list where the selected parameter was found is returned there. On failure a NULL is returned. A failure status may be returned with PPVL_errno set to PPVL_SUCCESS (0) which indicates that the Last_Parameter was found, but not The_Selection. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Aggregate is NULL. Select is not one of the valid values. The_Selectionis NULL and Select is either PPVL_SELECT_NAME or PPVL_SELECT_PARAMETER. PPVL_ERROR_NO_MEMORY Dynamic memory for parsable copy of the pathname (thus Select is set to PPVL_SELECT_NAME) could not be allocated. PPVL_ERROR_ILLEGAL_SYNTAX The_Aggregate is not a valid begin aggregate parameter. PPVL_ERROR_EMPTY_STATEMENT The Last_Parameter could not be found. PPVL_find_value - The value list of The_Array is first searched, recursively, for the value pointer with the Last_Value value. This identifies the starting point for the Type search which follows. If Last_Value is PPVL_SEARCH_FROM_THE_TOP (NULL), then the Type search starts with the first value in The_Array list. The search process is depth-wise; i.e. whenever an array is encountered in a value list it is searched before the remainder of the values in the list. This corresponds to the order in which values appear in PVL statements. When the the value with the specified Type is found it is checked for a match with the Data argument. When a successful match is made a pointer the value that matched is returned. A pointer to the parent array that contains the selected value in its list is returned through The_Parent - an address of a value pointer (PPVL_Value **) - if not NULL. The Type argument may be for a specific numeric type or any (specific or general) string or array type. It may also be the value PPVL_TYPE_ANY (0) in which case the next value, if any, after Last_Value is selected. When the value with the specified Type is found, its data value is compared against the Data argument (this does not occurs when Type is PPVL_TYPE_ANY). If The Data argument is PPVL_SELECT_ANY no data comparison is done (the value, regardless of its data, is selected). Otherwise the Data value is interpreted according the Type code: PPVL_TYPE_INTEGER - (unsigned long *)Data PPVL_TYPE_REAL - (double *)Data Any string type - (char *)Data Any array type - (PPVL_Value *)Data Carefully not the difference in how numeric and other Data arguments are treated: For numeric data the Data argument is the address of the variable, not the variable value itself. For other string types the Data argument is the string pointer variable value (char *), not the address of the string pointer variable. The same applies to array types for which the Data arguments are "PPVL_Value *", not "PPVL_Value **'. On success a pointer to the value with matching Type and Data is returned. If The_Parent is not NULL, a pointer to the array value that contains the matching value in its list is returned there. On failure NULL is returned. A failure status may be returned with PPVL_errno set to PPVL_SUCCESS (0) which indicates that the Last_Value was found but the value with matching Type and Data. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Array is NULL. PPVL_ERROR_ILLEGAL_SYNTAX The_Array is not a valid array type value. PPVL_ERROR_EMPTY_STATEMENT The Last_Value could not be found. * Output Functions PPVL_write_parameter PPVL_write_value These functions translate internal PPVL_Parameter or PPVL_Value data structures into external Parameter Value Language statements. The PVL that is generated conforms strictly to the PVL standards documents. PPVL_write_parameter - The contents of The_Parameter are written to The_File (stdout if NULL). If the content of The_Parameter is a value (PPVL_Value *) then PPVL_write_value is used to write its contents. If The_Parameter is an aggregate, then each parameter in its parameter list is recursively written as well. Any comments associated with the parameter are written before the PVL statement proper, also indented. Occurances of new-line characters ('\n') in the comment string cause separate comment lines to be written. The PVL statement is preceeded by Indent_Level horizontal tab characters. Each recursively processed parameter list causes the Indent_Level to be incremented. If, however, Indent_Level is negative no indenting is applied. Parameter names for aggregates are restored to their proper names according to their specific classification, and their internal names are written as the PVL string value identifying the aggregate. Parameter names with any embedded white space are quoted. At the end of each parameter list for an aggregate parameter a corresponding END_GROUP or END_OBJECT PVL statment is provided. When the parameter is an aggregate named "The Container", as returned from PPVL_read_aggregate and PPVL_scan_aggregate, a PVL END statement is written after the entire contents of the aggregate have been written. Thus writing out one of these aggregates produces a PVL statement list that is formally the same, if not exactly identical, to what was read in. On success a PPVL_Error_Code of PPVL_SUCCESS (0) is returned. On failure (which may be inherited from PPVL_write_value) the value of PPVL_errno is returned (which will be non-zero). A failure condition will also generate a comment line containing the error message describing the error code. Possible error conditions: PPVL_ERROR_BAD_ARGUMENT The_Parameter is NULL or its name is NULL. PPVL_write_value - The contents of The_Value are written to The_File (stdout if NULL). If The_Value is an array type, then each value in its list is recursively written. Proper set or sequence enclosures are provided for arrays, and between writing each parameter in an array list the comma (',') separator followed by a single space (' ') is written. Simple (non-array) values are written according to their type. Integer data is written in signed decimal notation unless the value's base is not 10, in which case radix notation is used: Base#Radix# The Base is The_Value's base value as a signed decimal integer. The radix is The_Value's data value represented in the corresponding base notation. Real data is written using the PPVL_real_number_format string to format the output. By default this is "%#G", but, since control of precision representation varies from system to system, PPVL_real_number_format is a global variable (char *) that may be assigned a different format string by the user. For example, "%#.15E" might be used to provide scientific notation with 15 digits of precision. The format string must be used cautiously, and only to format the appearance of the double precision real number data, or illegal PVL output could result. String data is enclosed in double quotes ('"') if the value is type PPVL_TYPE_TEXT, single quotes (''') if the value is type PPVL_TYPE_SYMBOL, and undecorated otherwise. On success a PPVL_Error_Code of PPVL_SUCCESS (0) is returned. On failure the value of PPVL_errno is returned (which will be non-zero). Possible error conditions: PPVL_ERROR_BAD_ARGUMENT The_Value is NULL or its type is NULL. PPVL_ERROR_VALUE_OVERFLOW The base of an integer type value is not in the range 2-36 inclusive (absolute value). * List Counting Functions PPVL_count_all_parameters PPVL_count_parameters PPVL_count_all_values PPVL_count_values PPVL_index_parameter PPVL_index_value These functions are used to obtain the number of entries in parameter or value lists, or the index to a particular parameter or value in a list. PPVL_count_all_parameters - The total number of parameters in The_Aggregate's parameter list, and recursively all aggregate parameters in that list, is returned. This function uses the PPVL_count_parameters function. It does not modify the value of PPVL_errno. PPVL_count_parameters - The number of parameters in The_Aggregate's parameter list, and only that list, is returned. On success a non-negative count value is returned. On failure a value of -1 is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Aggregate is NULL. PPVL_ERROR_ILLEGAL_SYNTAX The_Aggregate is not an aggregate parameter. PPVL_ERROR_EMPTY_STATEMENT The_Aggregate has no parameters. This is just a warning. A count of zero is returned. PPVL_count_all_values - The total number of values in The_Array's value list, and recursively all array values in that list, is returned. This function uses the PPVL_count_values function. It does not modify the value of PPVL_errno. PPVL_count_values - The number of values in The_Array's value list, and only that list, is returned. On success a non-negative count value is returned. On failure a value of -1 is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Array is NULL. PPVL_ERROR_ILLEGAL_SYNTAX The_Array is not an array type value. This is just a warning. A count of 1 is returned. PPVL_ERROR_EMPTY_STATEMENT The_Array has no values. This is just a warning. A count of 0 is returned. PPVL_index_parameter - The_Parameter is located in the parameter list of The_Aggregate and its index in this list is returned. Thus The_Parameter is found at The_Aggregate->content.parameters[index]. Parameter lists are always NULL terminated and so are usually processed with handle (pointer to parameter pointer) references, but there may be a need to use array index references for some applications. On success a non-negative index value is returned. On failure a value of -1 is returned. Possible error/warning conditions: PPVL_ERROR_BAD_ARGUMENT The_Aggregate is NULL. PPVL_ERROR_ILLEGAL_SYNTAX The_Aggregate is not an aggregate parameter. PPVL_ERROR_EMPTY_STATEMENT The_Parameter was not found in The_Aggregate parameter list. PPVL_index_value - This function works just like PPVL_index_parameter, but searches for The_Value in The_Array. * Error handling Every PPVL function sets the global int variable PPVL_errno. Initially it is set to PPVL_SUCCESS on entering the function. If an error condition is detected, it is set to an appropriate value before the function cleans up and returns an error status value. The condition, however, may be considered to be only a warning (this may depend on PPVL_strict being FALSE) in which case PPVL_errno is set accordingly but no error return is taken (though some recovery action may be taken). Note that only the first warning condition in a function is registered. If an error condition occurs after a warning condition has been encountered the error condition takes precedence over the warning. There are three levels of error codes (four if you count PPVL_SUCCESS, a.k.a PPVL_ERROR_NO_ERROR): typedef enum PPVL_error_code { PPVL_ERROR_NO_ERROR, Fatal errors: PPVL_ERROR_NO_MEMORY, PPVL_ERROR_BAD_ARGUMENT, PPVL_ERROR_READING_FILE, Syntax errors: PPVL_ERROR_MISSING_QUOTE_END, PPVL_ERROR_MISSING_COMMENT_END, PPVL_ERROR_MISSING_UNITS_END, PPVL_ERROR_RESERVED_CHARACTER, PPVL_ERROR_ILLEGAL_SYNTAX, PPVL_ERROR_EMPTY_STATEMENT, PPVL_ERROR_VALUE_OVERFLOW, PPVL_ERROR_ARRAY_CLOSURE_MISMATCH, Abstract (high level) usage problems: PPVL_ERROR_GROUP_VALUE, PPVL_ERROR_MISSING_AGGREGATE_END, PPVL_ERROR_AGGREGATE_CLOSURE_MISMATCH, PPVL_ERROR_SIZED_RECORDS, PPVL_ILLEGAL_ERROR_CODE } PPVL_Error_Code; Fatal errors are non-recoverable and always result in a failure status value from the function. Syntax errors are usually recoverable and are treated as warnings if PPVL_strict is FALSE. Abstract errors are treated like syntax errors. Every error code has a corresponding brief (one short line) descriptive error message contained in the global PPVL_errmsg strings array. The macro PPVL_ERROR_MESSAGE provides the description string for the current value of PPVL_errno (this is commonly used as a printf-type function argrument). * Examples Here is a program that creates a new aggregate parameter containing a few parameters with different types of values: #include "PPVL.h" main () { PPVL_Parameter *The_Aggregate; int value; double number; The_Aggregate = PPVL_add_parameter ( PPVL_add_parameter ( PPVL_add_parameter ( PPVL_add_parameter ( PPVL_new_parameter ( "The_Aggregate", PPVL_CLASS_BEGIN_GROUP, NULL, "**\n** The base aggregate. **\n**", NULL ), PPVL_new_parameter ( "Parameter_0", PPVL_CLASS_TOKEN, NULL, " A token ", NULL )), PPVL_new_parameter ( "Parameter_1", PPVL_CLASS_ASSIGNMENT, PPVL_new_value ( PPVL_TYPE_INTEGER, (value = 1, &value), 0, "int decimal" ), " A single decimal integer value = 1 ", NULL )), PPVL_new_parameter ( "Parameter_2", PPVL_CLASS_ASSIGNMENT, PPVL_add_value ( PPVL_add_value ( PPVL_add_value ( PPVL_new_value ( PPVL_TYPE_SET, NULL, 0, "set" ), PPVL_new_value ( PPVL_TYPE_IDENTIFIER, "IDENTIFIER", 0, NULL )), PPVL_new_value ( PPVL_TYPE_SYMBOL, "SYMBOL", 0, NULL )), PPVL_new_value ( PPVL_TYPE_TEXT, "A text value", 0, NULL )), " An array of three string values. ", NULL )), PPVL_new_parameter ( "Parameter_3", PPVL_CLASS_ASSIGNMENT, PPVL_new_value ( PPVL_TYPE_REAL, (number = 3.3, &number), 0, "double" ), " A single double number = 3.3 ", NULL ) ); PPVL_write_parameter (The_Aggregate, stdout, 0); exit (0); } The program, above, generates the following output (comment delimiters have been changed to "/-" and "-/" for this comment block): /-**-/ /-** The base aggregate. **-/ /-**-/ BEGIN_GROUP = The_Aggregate; /- A token -/ Parameter_0; /- A single decimal integer value = 1 -/ Parameter_1 = 1 ; /- An array of three string values. -/ Parameter_2 = {IDENTIFIER, 'SYMBOL', "A text value"} ; /- A single double number = 3.3 -/ Parameter_3 = 3.30000 ; END_GROUP = The_Aggregate; A common practice is to have a couple of PVL parameters near the beginning of the file label that indicate the record size and number for the label. These are obtained from a little bite of the file, and then used to read the entire label. Here is some sample code (sans error checking for clarity) that demonstrates this procedure: file = fopen (filename, "r"); The_Aggregate = PPVL_read_aggregate ( file, 512, NULL ); The_Parameter = PPVL_find_parameter ( The_Aggregate, PPVL_SEARCH_FROM_THE_TOP, PPVL_SELECT_NAME, "RECORD_BYTES", NULL ); count = The_Parameter->content.value->data.integer; The_Parameter = PPVL_find_parameter ( The_Aggregate, PPVL_SEARCH_FROM_THE_TOP, PPVL_SELECT_NAME, "LABEL_RECORDS", NULL ); count *= The_Parameter->content.value->data.integer; PPVL_free_parameter (The_Aggregate); rewind (file); The_Aggregate = PPVL_read_aggregate (file, count, NULL); PPVL_write_parameter (The_Aggregate, stdout, 0); Here's an even simpler example that (should) produce the same result (along with some error checking and processing info): file = fopen (filename, "r"); if (The_Aggregate = PPVL_read_aggregate ( file, PPVL_NO_READ_LIMIT, &count )) { printf ("PPVL_read_aggregate scanned %d characters.\n", count); if (PPVL_errno) printf ("Warning: %s\n", PPVL_ERROR_MESSAGE); PPVL_write_parameter (The_Aggregate, stdout, 0); } else fprintf (stderr, "PPVL_read_aggregate failed at character %d: %s\n", count, PPVL_ERROR_MESSAGE); Author: Bradford Castalia Castalia@Arizona.edu Senior Systems Analyst (520) 621-6946 Planetary Image Research Laboratory (520) 621-4824 Department of Planetary Sciences FAX: (520) 621-9628 University of Arizona Space Sciences Bldg. 1629 E. University Blvd. Room 429 Tucson, Arizona 85721-0092 "Build an image in your mind, fit yourself into it." The log of Cyradis seeress of Kell. PIRL SCCS ID: @(#)PPVL.c 1.8 10/04/00