X2Go::Server::Agent::NX::Options(3pm)

NAME
X2Go::Server::Agent::NX::Options - NX Options modification module

SYNOPSIS
 use X2Go::Server::Agent::NX::Options;

 # Options string, probably read in from somewhere, but
 # hardcoded here.
 my $options = 'some=option,another=opt,more=values,some=override,more=data:90';

 # Parse into an intermediate form.
 my $intermediate = X2Go::Server::Agent::NX::Options::parse_options ($options);

 # Check for errors.
 die "Unable to parse option string, aborting.\n" unless (defined ($intermediate));

 # (Optionally) compact it, this should make the duplicated "some" and "more"
 # keys unique.
 $intermediate = X2Go::Server::Agent::NX::Options::compact_intermediate ($intermediate);

 # Error handling ...
 die "Unable to compact intermediate array, aborting.\n" unless (defined ($intermediate));

 # Add to options string.
 my $transform_op = '+new=value';

 # Parse and sanitize transform string.
 my $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op);

 # Error handling ...
 die "Invalid transformation passed, aborting.\n" unless (defined ($interpreted_transform_ref));

 # Extract transformation data.
 my ($transform_mode, $sanitized_transform) = @{$interpreted_transform_ref};

 # Apply transformation.
 $intermediate = X2Go::Server::Agent::NX::Options::transform_intermediate ($intermediate, $transform_mode, $sanitized_transform);

 # Error handling ...
 die "Error while transforming intermediate representation, aborting.\n" unless (defined ($intermediate));

 # Try to remove a combination which doesn't exist, this should not modify the
 # intermediate.
 # No more comments for things that were already explained.
 $transform_op = '-another=doesnotexist';
 $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op);
 die "Invalid transformation passed, aborting.\n" unless (defined ($interpreted_transform_ref));
 ($transform_mode, $sanitized_transform) = @{$interpreted_transform_ref};
 $intermediate = X2Go::Server::Agent::NX::Options::transform_intermediate ($intermediate, $transform_mode, $sanitized_transform);
 die "Error while transforming intermediate representation, aborting.\n" unless (defined ($intermediate));

 # Remove a key unconditionally, this should change the intermediate.
 $transform_op = '-some';
 $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op);
 die "Invalid transformation passed, aborting.\n" unless (defined ($interpreted_transform_ref));
 ($transform_mode, $sanitized_transform) = @{$interpreted_transform_ref};
 $intermediate = X2Go::Server::Agent::NX::Options::transform_intermediate ($intermediate, $transform_mode, $sanitized_transform);
 die "Error while transforming intermediate representation, aborting.\n" unless (defined ($intermediate));

 # Modify/update a key.
 $transform_op = '+another=newval';
 $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op);
 die "Invalid transformation passed, aborting.\n" unless (defined ($interpreted_transform_ref));
 ($transform_mode, $sanitized_transform) = @{$interpreted_transform_ref};
 $intermediate = X2Go::Server::Agent::NX::Options::transform_intermediate ($intermediate, $transform_mode, $sanitized_transform);
 die "Error while transforming intermediate representation, aborting.\n" unless (defined ($intermediate));

 # Extract the "more" key.
 my $extract = X2Go::Server::Agent::NX::Options::extract_element ($intermediate, q{more});

 # Error handling ...
 die "Unable to extract 'more' key from intermediate, aborting.\n" unless defined ($extract);

 # Fetching multiple elements could be fine, for instance when the intermediate is not compact.
 # Hence, this need not be a general error, but we'll treat it as one here.
 die "Extract operation returned more than one element, this should not happen with a compacted intermediate, aborting.\n" if (1 < scalar (@{$extract}));

 # Likewise, it would be okay for the result to be empty, but not for us right here.
 die "Extract operation returned no result, aborting.\n" if (1 > scalar (@{$extract}));

 my $extracted_kv = q{};

 # Now, get the actual data in a presentable form.
 foreach my $entry (@{$extract}) {
   foreach my $key (%{$entry}) {
     $extracted_kv .= $key;
     my $value = $entry->{$key};
     if (defined ($value)) {
       $extracted_kv .= q{=} . $value;
     }
     last;
   }
   last;
 }

 # At this point, $extracted_kv should be "more=data".

 # Yet again, but this time extracting a key which does not exist.
 $extract =  X2Go::Server::Agent::NX::Options::extract_element ($intermediate, q{nosuchey});

 # Error handling ...
 die "Unable to extract 'nosuchkey' key from intermediate, aborting.\n" unless defined ($extract);

 # Should be empty.
 die "Extract operation returned a result, aborting.\n" if (0 < scalar (@{$extract}));

 # Transform back into a string.
 my $out = X2Go::Server::Agent::NX::Options::intermediate_to_string ($intermediate);

 # Error handling ...
 die "Unable to transform intermediate back into string, aborting.\n" unless (defined ($out));

 # At this point, $out should be 'another=newval,more=data,new=value:90'.

DESCRIPTION
Use this module to modify or extract data from X2Go/NX Agent options strings. Refer to "OPTIONS STRINGS" for an in-depth description of options strings.

First, transform the input options string into an intermediate representation via "parse_options". The options string must end with a display specification (i.e., it must end in ":displaynumber"). Parsing errors are indicated by it returning "undef". The returned value is actually a reference to an array of hash references, but you should make no assumptions to the layout or even its actual format. Treat it like a black box. Crucially, whenever an intermediate is expected, such a reference should be passed.

To remove redundant or empty entries within an options string, pass the intermediate to "compact_intermediate". This is entirely optional and can be done at any step, as long as an intermediate is available. If you intend to extract data, it is recommended, but not necessary, to compact the intermediate first.

In order to extract key-value pairs, supply the intermediate and a key-value pair to extract to "extract_element". Its result will be either undefined on error, or a reference to an array consisting of references to hashes. Each hash contains exactly one key-value pair.

The same approach using "extract_element" can be used to check for the existence of either keys or full key-value pairs. If the returned array is empty, no such element exists.

For compacted intermediates, each extraction operation returns an array with at most one hash reference entry. Non-compacted intermediates can contain a key multiple times, so no guarantee regarding the result's magnitude can be given.

To parse transformations, pass each one to "interpret_transform". Refer to "TRANSFORMATIONS" for documentation on transformation formats. This will either return "undef" on error, or an array of two scalars - the transformation mode (an internal number) and the sanitized transformation string (i.e., the original transformation string with any preceding operator removed).

Pass the intermediate, the transformation mode and the sanitized transformation string to "transform_intermediate" to modify the intermediate value.

Repeat this until the intermediate is modified to your liking.

Finally, pass the intermediate to "intermediate_to_string" in order to parse it back into a normal string. This operation is essentially the opposite of "parse_options". As usual, "undef" is returned on error.

Generally, parsing an options string to an intermediate via "parse_options" and then immediately parsing it back into a string via "intermediate_to_string" should always produce an options string that is identical to the original options string (assuming no errors occurred).

If this is not the case, please report a bug.

Any subroutines and constants not marked as exportable are explicitly not documented and should be regarded as internal and not be used. There is no guarantee regarding their behavior or existence.

OPTIONS STRINGS
X2Go/NX Agent options strings are fully documented in nxagent's documentation and additional, linked places therein.

This module is not really concerned with the actual content of an options string, but mostly its format.

An options string follows the form [[key[=value,]]:displaynumber.

This has some interesting implications:

•   Key-value pairs are entirely optional. For example, an options string like ":42" is well-formed.

•   Options strings must end with a display number separator, i.e., a colon (":") and a display number.

    No parsing is done on the display number, so any string (even the empty string) is valid as far as this module is concerned.

    The display number, however, can not contain a colon (":"), since that would make it a new display number separator.

    This module will parse the options string from right to left when searching for the display number separator and use the first one it can find.

•   Key-value pairs are separated via a comma (","). Hence, this character is not valid in neither keys nor values. As a workaround, it can be URL-encoded, as, e.g., %2C, and then used as part of either keys or values.

•   Key-value pairs can be empty. This is supported and empty key-value pairs will be preserved, but will trigger warnings at parse time.

    An options string such as ",,,:65" is hence valid.

    To remove such empty elements, use "compact_intermediate". An implicit empty element is added whenever the resulting options string would only contain the display number. This one can not be removed, but also won't show up anywhere. Adding any non-empty new key will automatically replace such an empty element, without any need for actual compaction.

•   In a key-value pair, keys and values are separated from each other via an equal sign ("=").

    Hence, this character is not a valid part of a key. It can, however, be a valid part of a value.

    To use this character as part of a key, it can be URL-encoded as, e.g., %3D.

    While it is legal as part of a value, it is recommended to also URL-encode it when used as part of a value in order to not confuse other parsers.

    An options string such as "this%3Dis%3Da=key:38" is just as valid as "this=is=a=key:38" or "this=is%3Da%3Dkey:38".

    However, the semantics differ. While the latter two key-value pairs are logically equivalent to each other, they are very much different from the first one.

    For the first case, the key will be "this%3Dis%3Da" (or, logically, also "this=is=a", which can not be directly represented), while the value will be just "key".

    The latter two will parse into a key "this" with a value of "is=a=key" (or, logically equivalent, "is%3Da%3Dkey").

•   Quoting with any character is unsupported. Quotes and other grouping characters (like curly braces ["{}"]) are seen verbatim without any special meaning.

•   Since options strings are typically parsed by C applications, "NUL" (control) characters are prematurely terminating the string and hence cannot be directly embedded. Indirectly, they can be embedded by URL-encoding them as %00.

    There is, however, no guarantee that an application unpacking such a string will be able to scan any data after the first embedded "NUL" character.

    It is highly recommended to avoid using embedded "NUL" characters.

    This module will not explicitly scan for them, and, hence, also not issue warnings related to those characters.

•   There are no provisions (other than the mentioned invalid characters) on the content of keys and values.

    Importantly, this also means that the same key can show up multiple times in the options string. Semantically, this is redundant, since only the last occurrence of a key (assuming the options string is parsed from left to right) will take any effect. Syntactically, it's completely legal, though.

    It is recommended to avoid duplicate keys in the input options string.

    Note that, due to the nature of the supported transformations, keys can not be duplicated with this module.

    To remove duplicated keys, use "compact_intermediate". This will preserve the order in a first-seen fashion.

    Additionally, most non-printable control characters can be used verbatim. This includes, but is not limited to, characters like "LF" ("\n"), "TAB" ("\t") and "VT" ("\v").

    Naturally, it is recommended to avoid such characters.

•   A key-value pair with an empty key but a non-empty value is allowed.

    Likewise, a key-value pair with a non-empty key, but an empty value is allowed. In this case, the value will be interpreted as an empty string in order to differentiate it from a non-existent value.

TRANSFORMATIONS
Transformations follow the form [+]|-key[=value], which means that:

•   They can be prefixed with a plus character ("+") to indicate either additions or modifications. A missing prefix character is interpreted like a plus character.

    If the given key already exists in the intermediate, the key-value pair will be updated with the provided value (if any), or a new key-value pair added.

    Insertions always take place at the end of the intermediate.

    The value can be omitted, in which case key will be added without a value on insertions or a potentially existing value removed on updates.

•   If they are prefixed with a minus character ("-"), deletion mode is activated.

    If the given key is not part of the intermediate, no deletion will occur.

    Otherwise, the optional value determines deletion: if no value has been provided, key will be removed from the intermediate regardless of its value. If the optional value has been provided, key will only be removed if both values match.

AUTHOR
This manual has been written by Mihai Moldovan <ionic@ionic.de> <mailto:ionic@ionic.de> for the X2Go project (<https://www.x2go.org>).