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 Planetary 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,8), June 2000 [CCSDS 641.0-B-2] and Green Book "Parameter Value Language - A Tutorial", May 1992 [CCSDS 641.0-G-2] documents.
The language has a deceptively simple syntax for a parameter defining PVL statement:
Name = Value <Units>
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 extended 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.
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, the
PIRL implementation of the language parser is designed to be very tolerant, rather
than requiring strict adherence 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
PPVL_strict
is 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).
The parser stores the information it gathers about each parameter definition in a PPVL_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 particular classification
:
PPVL_CLASS_UNKNOWN
)
PPVL_CLASS_TOKEN
)
PPVL_CLASS_END
)
PPVL_CLASS_ASSIGNMENT
)
PPVL_CLASS_AGGREGATE
or
PPVL_CLASS_BEGIN_AGGREGATE
)
Object
(PPVL_CLASS_OBJECT
)
Begin_Object
or BeginObject
(PPVL_CLASS_BEGIN_OBJECT
)
End_Object
or EndObject
(PPVL_CLASS_END_OBJECT
)
Group
(PPVL_CLASS_GROUP
)
Begin_Group
or BeginGroup
(PPVL_CLASS_BEGIN_GROUP
)
End_Group
EndGroup
(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 - 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 pattern
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_Class
macros are provided for ease
in
evaluating classification codes. For Class
substitute any
(except Unknown)
of the
classification names (as in PPVL_CLASS_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_data
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
).
The content
element of a PPVL_Parameter
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 (char *
), or itself an
array
of value
structures. In this latter case, data.array
points to a
NULL
terminated list
of PPVL_Value
pointers.
Note: Values in the list of an array may be a mix of any types. This includes another array value. Arrays may be nested to any depth. There is also no limitation on the size of any array, including subarrays of different sizes.
The data type of each value is indicated by a
type
code:
PPVL_TYPE_UNKNOWN
PPVL_TYPE_NUMERIC
PPVL_TYPE_INTEGER
(unsigned long
)
PPVL_TYPE_REAL
(double
)
PPVL_TYPE_STRING
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 a
"-
" and/or ":
").
PPVL_TYPE_ARRAY
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_Type
macros are provided for ease in
evaluating type
codes.
For Type substitute any (except Unknown) of the type names (as in
PPVL_TYPE_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.
The base
is used when an integer value is output to
determine what numerical base will be used to represent it.
See the description of the
PPVL_write_value
function for
details.
When a parameter is of the Begin_Aggregate
classification
- Object, Begin_Object (or BeginObject), Group, or Begin_Group (or
BeginGroup) parameter name - 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 automatically
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).
A complete list of the PPVL functions and their purpose is in the Function Index, which links to the specific function description below. Each function description provides its calling syntax. Every source code file using the PPVL module should include the module header:
#include "PPVL.h"
A pseudo-object-oriented interface is also provided. These functions
(actually
macros employing the specific functions) operate on typeless
PPVL_Object
handles, which
may be
PPVL_Parameter
or
PPVL_Value
pointers. The macros
PPVL_Is_Parameter
and PPVL_Is_Value
determine
the specific type of a PPVL_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:
These functions manage the conversion of external PVL statements 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_Parameter * PPVL_read_aggregate ( FILE *File, unsigned long Read_Limit, unsigned long *Total_Scanned );
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):
End
(PPVL_CLASS_END
) parameter is
encountered.
Read_Limit
(measured in number of bytes) is
reached.
Sized (a.k.a. variable) record formatted files, in which each record is
preceded 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_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_Parameter * PPVL_scan_aggregate ( char *The_String, char **Next_Character );
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_Parameter * PPVL_scan_parameter ( char *The_String, char **Next_Character );
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
occurred 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 obtain 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
(or BeginObject
),
End_Object
(or EndObject
), Group
,
Begin_Group
(or BeginGroup
), or
End_Group
(or EndGroup
) - 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
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_EMPTY_STATEMENT
PPVL_ERROR_ILLEGAL_SYNTAX
PPVL_ERROR_RESERVED_CHARACTER
PPVL_ERROR_GROUP_VALUE
PPVL_Value * PPVL_scan_value ( char *The_String, char **Next_Character );
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 occurred
otherwise.
It is initially assumed that the value being assembled will be an array, so
a list of
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
NULL
.
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_ILLEGAL_SYNTAX
'='
), 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.
'{'
, '('
)
was encountered when a datum
was expected.
PPVL_ERROR_ARRAY_CLOSURE_MISMATCH
'}'
, ')'
) is missing.
PPVL_Error_Code PPVL_scan_datum ( PPVL_Value *The_Value, char *The_String, char **Next_Character );
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 '-'.For example:
Base is an integer (2-36) that is the base of the value.
Radix is an integer value with the specified Base.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 has any embedded escape sequences converted to special
format control characters, unless PPVL_verbatim_strings is TRUE, and is
then 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 not
NULL
) on success; the
location in The_String
where the error occurred otherwise. Possible
error/warning conditions:
PPVL_ERROR_BAD_ARGUMENT
The_Value
or The_String
is NULL
.
PPVL_ERROR_EMPTY_STATEMENT
PPVL_ERROR_ILLEGAL_SYNTAX
PPVL_ERROR_VALUE_OVERFLOW
PPVL_ERROR_RESERVED_CHARACTER
char * PPVL_scan_quoted_string ( char *The_String, char **Next_Character );
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 escape sequence format characters which are converted to special listing format control characters that will appear correctly when written:
\n
- line break.
\t
- horizontal tab.
\f
- form feed (page break).
\\
- backslash character.
\X
- the character X.
\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 preceding 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 prevent escape sequence
conversion to special characters and override the normal hyphen 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 occurrence of the quote character is returned
through Next_Character
(if not NULL
) on success; the
location in The_String
where the error occurred otherwise. Possible error/warning conditions:
PPVL_ERROR_BAD_ARGUMENT
The_String
is NULL
.
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_MISSING_QUOTE_END
char * PPVL_scan_units ( char *The_String, char **Next_Character );
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 not NULL
) on
success; the location in
The_String
where the error occurred otherwise. Possible
error/warning
conditions:
PPVL_ERROR_BAD_ARGUMENT
The_String
is NULL
.
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_ILLEGAL_SYNTAX
PPVL_ERROR_MISSING_UNITS_END
char * PPVL_scan_comments ( char *The_String, char **Next_Character );
The_String
is scanned for C-language style comments. A
comment string starts, after any whitespace (and crosshatch comments if
not in strict mode; see
PPVL_skip_white_space_and_comments
), 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'
) character 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 encountered, 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
not NULL
) on success; the location in The_String
where the error occurred
otherwise. Possible error/warning conditions:
PPVL_ERROR_BAD_ARGUMENT
The_String
is NULL
.
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_MISSING_COMMENT_END
PPVL_skip_white_space_and_comments
char * PPVL_skip_white_space_and_comments ( char *The_String );
The_String
is scanned for contiguous white space
characters and comment strings in search of the next occurrence of a
character that is neither whitespace nor the start of a comment.
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
. As a special case, crosshatch
comments - from a '#' following any whitespace up to the end of the line
- are also skipped unless strict mode is in effect.
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
char * PPVL_comb_symbol ( char *The_String );
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
.
These functions are used to manage the complete life cycle of
PPVL_Parameter
and
PPVL_Value
objects.
PPVL_Parameter * PPVL_new_parameter ( char *name, PPVL_Class classification, PPVL_Object *content, char *comments, void *user_data );
A new
PPVL_Parameter
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 preceding the parameter statement. The
user_data
is anything the user wants to attach to the
parameter. Note 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
classification
argument is specified that is not a
recognized
parameter classification.
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_ILLEGAL_SYNTAX
PPVL_Value * PPVL_new_value ( PPVL_Type type, PPVL_Object *data, int base, char *units );
Like
PPVL_new_parameter
, a new
PPVL_Value
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
Note: There is a 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
data
, base
, or
units
argument is specified, but the
type
argument is zero.
type
argument is not a recognized value type.
base
argument is non-zero and its absolute value
is not in the range 2-36 inclusive.
PPVL_ERROR_NO_MEMORY
PPVL_ERROR_ILLEGAL_SYNTAX
type
is for an array, but the data
is not a value object.
PPVL_Object * PPVL_new ( /* Parameter Value --------- ----- */ int Classification, Type, PPVL_Object *Content, *Content, char *Name, (int)Base, char *Comments, *Units void *User_Data );
This macro creates either a
PPVL_Parameter
using
PPVL_new_parameter
or a
PPVL_Value
using
PPVL_new_value
, depending on the initial
Classification
/Type
argument.
Note: When creating a PPVL_Parameter, the order of arguments
is not the same as PPVL_new_parameter
. When creating
a PPVL_Value
the third
argument is expected to be an integer.
PPVL_Parameter * PPVL_duplicate_parameter ( PPVL_Parameter *The_Parameter );
A new
PPVL_Parameter
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
PPVL_ERROR_ILLEGAL_SYNTAX
The_Parameter
is not a pointer to a valid
PPVL_Parameter
structure.
PPVL_Value * PPVL_duplicate_value ( PPVL_Value *The_Value );
Just like
PPVL_duplicate_parameter
, but for
PPVL_Value
structures.
PPVL_Object * PPVL_duplicate ( PPVL_Object *The_Object );
This macro will recognize The_Object
as a
PPVL_Parameter
or a
PPVL_Value
and use either
PPVL_duplicate_parameter
or
PPVL_duplicate_value
, respectively,
to create a duplicate.
PPVL_Parameter * PPVL_add_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *The_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: 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
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_Value * PPVL_add_value ( PPVL_Value *The_Array, PPVL_Value *The_Value );
Just like
PPVL_add_parameter
, but for
PPVL_Value
structures.
PPVL_Object * PPVL_add ( PPVL_Object *The_Container, PPVL_Object *The_Object );
This macro will recognize The_Container
as an aggregate
or an array and The_Object
as a parameter or a value and use either
PPVL_add_parameter
or
PPVL_add_value
, respectively, to add the later
to the former.
PPVL_Parameter * PPVL_remove_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *The_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
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_Value * PPVL_remove_value ( PPVL_Value *The_Array, PPVL_Value *The_Value );
Just like
PPVL_remove_parameter
, but for
PPVL_Value
structures.
PPVL_Object * PPVL_remove ( PPVL_Object *The_Container, PPVL_Object *The_Object );
This macro will recognize The_Container
as an aggregate
or an array and The_Object
as a parameter or a value and use either
PPVL_remove_parameter
or
PPVL_remove_value
, respectively, to remove the later
from the former.
void PPVL_free_parameter ( PPVL_Parameter *The_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.
void PPVL_free_value ( PPVL_Value *The_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.
void PPVL_free ( PPVL_Object *The_Object );
This macro will recognize The_Object
as a
PPVL_Parameter
or a
PPVL_Value
and use either
PPVL_free_parameter
or
PPVL_free_value
, respectively,
to free the dynamically allocated memory associated with
The_Object
.
These functions are used to search
PPVL_Parameter
or
PPVL_Value
objects for
selected elements with specified values.
PPVL_Parameter * PPVL_find_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *Last_Parameter, PPVL_Select Select, PPVL_Object *The_Selection, PPVL_Parameter **The_Parent );
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,
if
not NULL
, an address of a parameter pointer
(PPVL_Parameter **
).
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 occurrence 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
occurrence 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 parameter list is
selected. This is typically used to traverse back up the parameter
hierarchy 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_Selection
is NULL
and
Select
is either PPVL_SELECT_NAME
or
PPVL_SELECT_PARAMETER
.
PPVL_ERROR_NO_MEMORY
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
Last_Parameter
could not be found.
PPVL_Value * PPVL_find_value ( PPVL_Value *The_Array, PPVL_Value *Last_Value, PPVL_Type Type, PPVL_Object *Data, PPVL_Value **The_Parent );
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, and, if not NULL
, 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 **
).
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
Note: There is a 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 not 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
Last_Value
could not be found.
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 ); PPVL_Object *
This macro will recognize The_Container
as an aggregate
or an array and use either
PPVL_find_parameter
or
PPVL_find_value
, respectively, to find
The_Selection
(known as the
Data
to PPVL_find_value
) based on the
Select
criteria (known as the
Type
to PPVL_find_value
).
Last_Object
and
The_Parent
are used as expected by the
find functions.
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 PDS PVL standards documents.
PPVL_Error_Code PPVL_write_parameter ( PPVL_Parameter *The_Parameter, FILE *The_File, int Indent_Level );
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. Occurrences of new-line characters
('\n'
) in
the comment string cause separate comment lines to be written.
The PVL statement is preceded 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 EndGroup
) or
End_Object
(or EndObject
) PVL statement 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_Error_Code PPVL_write_value ( PPVL_Value *The_Value, FILE *The_File );
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
char* PPVL_value_string ( PPVL_Value *The_Value );
An attempt is made to convert The_Value
data to a
string representation. The conversion follows the same rules as for the
PPVL_write_value
function
except: no quoting is applied to a String value, only the data is
represented (no units), and an Array is not converted.
On success a dynamically allocated string (char*) containing The_Value
representation is returned. On failure NULL
is returned.
Failure occurs when The_Value
is an array type, the
base of an integer type value is not in the range 2-36 inclusive
(absolute value), or no dynamic memory is available.
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.
int PPVL_count_all_parameters ( PPVL_Parameter *The_Aggregate );
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
.
int PPVL_count_parameters ( PPVL_Parameter *The_Aggregate );
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.
int PPVL_count_all_values ( PPVL_Value *The_Array );
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
.
The number of values in
The_Array
's value list, and only that list, is
returned.
int PPVL_count_values ( PPVL_Value *The_Array );
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.
int PPVL_count_all ( PPVL_Object *The_Container );
This macro will recognize
The_Container
as an aggregate
or an array
and use either
PPVL_count_all_parameters
or
PPVL_count_all_values
, respectively,
to provide a count of all the parameters or values it - or,
recursively, any container in it - contains.
int PPVL_count ( PPVL_Object *The_Container );
This macro will recognize
The_Container
as an aggregate
or an array
and use either
PPVL_count_parameters
or
PPVL_count_values
, respectively,
to provide a count of all the parameters or values it contains.
int PPVL_index_parameter ( PPVL_Parameter *The_Aggregate, PPVL_Parameter *The_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.
int PPVL_index_value ( PPVL_Value *The_Array, PPVL_Value *The_Value );
This function works just like
PPVL_index_parameter
, but searches for
The_Value
in The_Array
.
int PPVL_index ( PPVL_Object *The_Container PPVL_Object *The_Object );
This macro will recognize
The_Container
as an aggregate
or an array
and use either
PPVL_index_parameter
or
PPVL_index_value
, respectively,
to provide the array index of The_Object
in the container's contents list.
Almost every PPVL function sets the global integer 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 argument).
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.
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:
/****/ /*** 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"} <set>; /* A single double number = 3.3 */ Parameter_3 = 3.30000 <double>; 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);