.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "X2Go::Server::Agent::NX::Options 3pm" .TH X2Go::Server::Agent::NX::Options 3pm "2023-09-23" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" X2Go::Server::Agent::NX::Options \- NX Options modification module .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use X2Go::Server::Agent::NX::Options; \& \& # Options string, probably read in from somewhere, but \& # hardcoded here. \& my $options = \*(Aqsome=option,another=opt,more=values,some=override,more=data:90\*(Aq; \& \& # 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.\en" 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.\en" unless (defined ($intermediate)); \& \& # Add to options string. \& my $transform_op = \*(Aq+new=value\*(Aq; \& \& # 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.\en" 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.\en" unless (defined ($intermediate)); \& \& # Try to remove a combination which doesn\*(Aqt exist, this should not modify the \& # intermediate. \& # No more comments for things that were already explained. \& $transform_op = \*(Aq\-another=doesnotexist\*(Aq; \& $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op); \& die "Invalid transformation passed, aborting.\en" 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.\en" unless (defined ($intermediate)); \& \& # Remove a key unconditionally, this should change the intermediate. \& $transform_op = \*(Aq\-some\*(Aq; \& $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op); \& die "Invalid transformation passed, aborting.\en" 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.\en" unless (defined ($intermediate)); \& \& # Modify/update a key. \& $transform_op = \*(Aq+another=newval\*(Aq; \& $interpreted_transform_ref = X2Go::Server::Agent::NX::Options::interpret_transform ($transform_op); \& die "Invalid transformation passed, aborting.\en" 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.\en" 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 \*(Aqmore\*(Aq key from intermediate, aborting.\en" 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\*(Aqll treat it as one here. \& die "Extract operation returned more than one element, this should not happen with a compacted intermediate, aborting.\en" 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.\en" 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 \*(Aqnosuchkey\*(Aq key from intermediate, aborting.\en" unless defined ($extract); \& \& # Should be empty. \& die "Extract operation returned a result, aborting.\en" 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.\en" unless (defined ($out)); \& \& # At this point, $out should be \*(Aqanother=newval,more=data,new=value:90\*(Aq. .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Use this module to modify or extract data from \fBX2Go/NX Agent\fR options strings. Refer to \*(L"\s-1OPTIONS STRINGS\*(R"\s0 for an in-depth description of options strings. .PP First, transform the input options string into an intermediate representation via \f(CW\*(C`parse_options\*(C'\fR. The options string \fImust\fR end with a display specification (i.e., it must end in \f(CW\*(C`:displaynumber\*(C'\fR). Parsing errors are indicated by it returning \f(CW\*(C`undef\*(C'\fR. The returned value is actually a \fIreference\fR to an \fIarray\fR of \fIhash\fR \&\fIreferences\fR, but you should make no assumptions to the layout or even its actual format. Treat it like a black box. Crucially, whenever an \fIintermediate\fR is expected, such a \fIreference\fR should be passed. .PP To remove redundant or empty entries within an options string, pass the \&\fIintermediate\fR to \f(CW\*(C`compact_intermediate\*(C'\fR. This is entirely optional and can be done at any step, as long as an \&\fIintermediate\fR is available. If you intend to extract data, it is recommended, but not necessary, to compact the \fIintermediate\fR first. .PP In order to extract key-value pairs, supply the \fIintermediate\fR and a \&\fIkey-value pair\fR to extract to \f(CW\*(C`extract_element\*(C'\fR. 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. .PP The same approach using \f(CW\*(C`extract_element\*(C'\fR 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. .PP For compacted \fIintermediates\fR, each extraction operation returns an array with at most one hash reference entry. Non-compacted \fIintermediates\fR can contain a key multiple times, so no guarantee regarding the result's magnitude can be given. .PP To parse transformations, pass each one to \f(CW\*(C`interpret_transform\*(C'\fR. Refer to \*(L"\s-1TRANSFORMATIONS\*(R"\s0 for documentation on transformation formats. This will either return \f(CW\*(C`undef\*(C'\fR 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). .PP Pass the \fIintermediate\fR, the \fItransformation mode\fR and the \&\fIsanitized transformation string\fR to \f(CW\*(C`transform_intermediate\*(C'\fR to modify the intermediate value. .PP Repeat this until the \fIintermediate\fR is modified to your liking. .PP Finally, pass the \fIintermediate\fR to \f(CW\*(C`intermediate_to_string\*(C'\fR in order to parse it back into a normal string. This operation is essentially the opposite of \f(CW\*(C`parse_options\*(C'\fR. As usual, \f(CW\*(C`undef\*(C'\fR is returned on error. .PP Generally, parsing an options string to an intermediate via \f(CW\*(C`parse_options\*(C'\fR and then immediately parsing it back into a string via \&\f(CW\*(C`intermediate_to_string\*(C'\fR \fIshould\fR always produce an options string that is identical to the original options string (assuming no errors occurred). .PP If this is not the case, please report a bug. .PP Any subroutines and constants not marked as exportable are \&\fBexplicitly not documented\fR and should be regarded as internal and \&\fBnot be used\fR. There is \fBno\fR guarantee regarding their behavior or existence. .SH "OPTIONS STRINGS" .IX Header "OPTIONS STRINGS" \&\fBX2Go/NX Agent\fR options strings are fully documented in nxagent's documentation and additional, linked places therein. .PP This module is not really concerned with the actual content of an options string, but mostly its format. .PP An options string follows the form [[\fIkey\fR[\fB=\fR\fIvalue\fR,]]\fB:\fR\fIdisplaynumber\fR. .PP This has some interesting implications: .IP "\(bu" 4 Key-value pairs are entirely optional. For example, an options string like \f(CW\*(C`:42\*(C'\fR is well-formed. .IP "\(bu" 4 Options strings \fImust\fR end with a display number separator, i.e., a \fBcolon\fR (\f(CW\*(C`:\*(C'\fR) and a display number. .Sp No parsing is done on the display number, so any string (even the empty string) is valid as far as this module is concerned. .Sp The display number, however, \fIcan not\fR contain a \fBcolon\fR (\f(CW\*(C`:\*(C'\fR), since that would make it a new display number separator. .Sp 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. .IP "\(bu" 4 Key-value pairs are separated via a \fBcomma\fR (\f(CW\*(C`,\*(C'\fR). Hence, this character is not valid in neither keys nor values. As a workaround, it can be URL-encoded, as, e.g., \f(CW%2C\fR, and then used as part of either keys or values. .IP "\(bu" 4 Key-value pairs can be empty. This is supported and empty key-value pairs will be preserved, but will trigger warnings at parse time. .Sp An options string such as \f(CW\*(C`,,,:65\*(C'\fR is hence valid. .Sp To remove such empty elements, use \f(CW\*(C`compact_intermediate\*(C'\fR. An implicit empty element is added whenever the resulting options string would only contain the display number. This one \fIcan not\fR 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. .IP "\(bu" 4 In a key-value pair, keys and values are separated from each other via an \&\fBequal sign\fR (\f(CW\*(C`=\*(C'\fR). .Sp Hence, this character is not a valid part of a key. It can, however, be a valid part of a value. .Sp To use this character as part of a key, it can be URL-encoded as, e.g., \&\f(CW%3D\fR. .Sp 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. .Sp An options string such as \f(CW\*(C`this%3Dis%3Da=key:38\*(C'\fR is just as valid as \&\f(CW\*(C`this=is=a=key:38\*(C'\fR or \f(CW\*(C`this=is%3Da%3Dkey:38\*(C'\fR. .Sp 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. .Sp For the first case, the \fIkey\fR will be \f(CW\*(C`this%3Dis%3Da\*(C'\fR (or, logically, also \&\f(CW\*(C`this=is=a\*(C'\fR, which can not be directly represented), while the \fIvalue\fR will be just \f(CW\*(C`key\*(C'\fR. .Sp The latter two will parse into a \fIkey\fR \f(CW\*(C`this\*(C'\fR with a \fIvalue\fR of \f(CW\*(C`is=a=key\*(C'\fR (or, logically equivalent, \f(CW\*(C`is%3Da%3Dkey\*(C'\fR). .IP "\(bu" 4 Quoting with any character is unsupported. Quotes and other grouping characters (like \fBcurly braces\fR [\f(CW\*(C`{}\*(C'\fR]) are seen verbatim without any special meaning. .IP "\(bu" 4 Since options strings are typically parsed by C applications, \f(CW\*(C`NUL\*(C'\fR (control) characters are prematurely terminating the string and hence cannot be directly embedded. Indirectly, they can be embedded by URL-encoding them as \f(CW%00\fR. .Sp There is, however, no guarantee that an application unpacking such a string will be able to scan any data after the first embedded \f(CW\*(C`NUL\*(C'\fR character. .Sp It is highly recommended to avoid using embedded \f(CW\*(C`NUL\*(C'\fR characters. .Sp This module will not explicitly scan for them, and, hence, also not issue warnings related to those characters. .IP "\(bu" 4 There are no provisions (other than the mentioned invalid characters) on the content of keys and values. .Sp 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. .Sp It is recommended to avoid duplicate keys in the input options string. .Sp Note that, due to the nature of the supported transformations, keys can not be duplicated with this module. .Sp To remove duplicated keys, use \f(CW\*(C`compact_intermediate\*(C'\fR. This will preserve the order in a first-seen fashion. .Sp Additionally, most non-printable control characters can be used verbatim. This includes, but is not limited to, characters like \f(CW\*(C`LF\*(C'\fR (\f(CW\*(C`\en\*(C'\fR), \f(CW\*(C`TAB\*(C'\fR (\f(CW\*(C`\et\*(C'\fR) and \f(CW\*(C`VT\*(C'\fR (\f(CW\*(C`\ev\*(C'\fR). .Sp Naturally, it is recommended to avoid such characters. .IP "\(bu" 4 A key-value pair with an empty key but a non-empty value is allowed. .Sp 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. .SH "TRANSFORMATIONS" .IX Header "TRANSFORMATIONS" Transformations follow the form [\fB+\fR]|\fB\-\fR\fIkey\fR[\fB=\fR\fIvalue\fR], which means that: .IP "\(bu" 4 They can be prefixed with a \fBplus\fR character (\f(CW\*(C`+\*(C'\fR) to indicate either additions or modifications. A missing prefix character is interpreted like a \fBplus\fR character. .Sp If the given \fIkey\fR already exists in the intermediate, the key-value pair will be updated with the provided \fIvalue\fR (if any), or a new key-value pair added. .Sp Insertions always take place at the end of the intermediate. .Sp The \fIvalue\fR can be omitted, in which case \fIkey\fR will be added without a value on insertions or a potentially existing value removed on updates. .IP "\(bu" 4 If they are prefixed with a \fBminus\fR character (\f(CW\*(C`\-\*(C'\fR), deletion mode is activated. .Sp If the given \fIkey\fR is not part of the intermediate, no deletion will occur. .Sp Otherwise, the optional \fIvalue\fR determines deletion: if no value has been provided, \fIkey\fR will be removed from the intermediate regardless of its value. If the optional \fIvalue\fR has been provided, \fIkey\fR will only be removed if both values match. .SH "AUTHOR" .IX Header "AUTHOR" This manual has been written by Mihai Moldovan for the X2Go project ().