parmacl/
lib.rs

1#![warn(missing_docs)]
2//! A command line parser for Rust
3//! 
4//! The primary purpose of this library is to parse a full command line. That is, a string containing the complete command line.
5//! It can also parse rust environment command line arguments as supplied by Rust (`std::env::Args`) however there are other libraries which
6//! specialise in this and may be a better choice.
7//! 
8//! While the Rust standard library does not give access to the full environment command line, this library could be used to
9//! parse internal commands entered within an application.
10//! 
11//! See [Features](#features).
12//! 
13//! # Usage
14//! 
15//! Follow the steps below to parse a command line:
16//! 1. Create an enum with one variant for each type of option argument expected in the command line.
17//! 1. Create an enum with one variant for each type of parameter argument expected in the command line.
18//! 1. Create an instance of [parmacl::Parser](Parser).
19//! 1. If necessary, set relevant properties of the Parser instance to reflect the style of the command line.
20//! 1. Add a [matcher](Matcher) for all possible arguments to the parser. Tag each matcher with the appropriate enum.
21//! 1. Call [Parser.parse_line(command_line)](Parser::parse_line) which will parse the command line and return a result containing
22//! either a [vector of parsed arguments](Args) or an error.
23//! 1. Loop through returned arguments and process each. The arguments are ordered by appearance in the command line.
24//! 
25//! ## Example
26//! 
27//! ```
28//!use parmacl::{Parser, Arg, RegexOrText, OptionHasValue};
29//!
30//!const LINE: &str = r#""binary name" -a "1st ""Param""" -B optValue "param2" -c "C OptValue""#;
31//! 
32//!#[derive(Default)]
33//!enum OptionEnum {
34//!    #[default] A,
35//!    B,
36//!    C,
37//!}
38//!#[derive(Default)]
39//!enum ParamEnum {
40//!    #[default] Param1,
41//!    Param2,
42//!}
43//! 
44//!let mut parser: Parser<OptionEnum, ParamEnum> = Parser::new();
45//! 
46//!parser
47//!    .push_new_option_matcher("optionA")
48//!        .set_option_tag(OptionEnum::A)
49//!        .some_option_codes(&[RegexOrText::with_text("a")]);
50//! 
51//!parser
52//!    .push_new_option_matcher("optionB")
53//!        .set_option_tag(OptionEnum::B)
54//!        .some_option_codes(&[RegexOrText::with_text("b")])
55//!        .set_option_has_value(OptionHasValue::IfPossible);
56//! 
57//!parser
58//!    .push_new_option_matcher("optionC")
59//!        .set_option_tag(OptionEnum::C)
60//!        .some_option_codes(&[RegexOrText::with_text("c")])
61//!        .set_option_has_value(OptionHasValue::Always);
62//! 
63//!parser
64//!     .push_new_param_matcher("param1")
65//!         .set_param_tag(ParamEnum::Param1)
66//!         .some_param_indices(&[0]);
67//! 
68//!parser
69//!    .push_new_param_matcher("param2")
70//!        .set_param_tag(ParamEnum::Param2)
71//!        .some_param_indices(&[1]);
72//! 
73//!let args = parser.parse_line(LINE).unwrap();
74//! 
75//!assert_eq!(args.len(), 6);
76//! 
77//!for arg in args {
78//!    match arg {
79//!        Arg::Binary(properties) => {
80//!            assert_eq!(properties.arg_index, 0);
81//!            assert_eq!(properties.value_text, "binary name");
82//!            assert_eq!(properties.char_index, 0);
83//!        },
84//!        Arg::Option(properties) => {
85//!            match properties.matcher.option_tag() {
86//!                OptionEnum::A => {
87//!                    // Process option A
88//!                    assert_eq!(properties.matcher.name(), "optionA");
89//!                    assert_eq!(properties.arg_index, 1);
90//!                    assert_eq!(properties.option_index, 0);
91//!                    assert_eq!(properties.code, "a");
92//!                    assert_eq!(properties.value_text, None);
93//!                    assert_eq!(properties.char_index, 14);
94//!                },
95//!                OptionEnum::B => {
96//!                    // Process option B
97//!                },
98//!                OptionEnum::C => {
99//!                    // Process option C
100//!                },
101//!            }
102//!        }
103//!        Arg::Param(properties) => {
104//!            match properties.matcher.param_tag() {
105//!                ParamEnum::Param1 => {
106//!                    // Process parameter Param1
107//!                    assert_eq!(properties.matcher.name(), "param1");
108//!                    assert_eq!(properties.arg_index, 2);
109//!                    assert_eq!(properties.param_index, 0);
110//!                    assert_eq!(properties.value_text, "1st \"Param\"");
111//!                    assert_eq!(properties.char_index, 17);
112//!                },
113//!                ParamEnum::Param2 => {
114//!                    // Process parameter Param2
115//!                },
116//!            }
117//!        }
118//!    }
119//!}
120//!```
121//! # Parsing environment arguments
122//! 
123//! Parmacl can also be used to parse the environment command line arguments passed to an application. The above example could be used to parse
124//! environment arguments with the following changes.
125//! 
126//! Instead of using the [new()](Parser::new) constructor function, use the [with_env_args_defaults()](Parser::with_env_args_defaults) constructor.
127//! This will create an instance of Parser with defaults suitable for parsing environment arguments.
128//!```
129//!# use parmacl::{Parser, Arg, RegexOrText, OptionHasValue};
130//!#
131//!# const LINE: &str = r#""binary name" -a "1st ""Param""" -B optValue "param2" -c "C OptValue""#;
132//!#  
133//!# #[derive(Default)]
134//!# enum OptionEnum {
135//!#     #[default] A,
136//!#     B,
137//!#     C,
138//!# }
139//!# #[derive(Default)]
140//!# enum ParamEnum {
141//!#     #[default] Param1,
142//!#     Param2,
143//!# }
144//!#  
145//! let mut parser: Parser<OptionEnum, ParamEnum> = Parser::with_env_args_defaults();
146//!#  
147//!# parser
148//!#     .push_new_option_matcher("optionA")
149//!#         .set_option_tag(OptionEnum::A)
150//!#         .some_option_codes(&[RegexOrText::with_text("a")]);
151//!#  
152//!# parser
153//!#     .push_new_option_matcher("optionB")
154//!#         .set_option_tag(OptionEnum::B)
155//!#         .some_option_codes(&[RegexOrText::with_text("b")])
156//!#         .set_option_has_value(OptionHasValue::IfPossible);
157//!#  
158//!# parser
159//!#     .push_new_option_matcher("optionC")
160//!#         .set_option_tag(OptionEnum::C)
161//!#         .some_option_codes(&[RegexOrText::with_text("c")])
162//!#         .set_option_has_value(OptionHasValue::Always);
163//!#  
164//!# parser
165//!#      .push_new_param_matcher("param1")
166//!#          .set_param_tag(ParamEnum::Param1)
167//!#          .some_param_indices(&[0]);
168//!#  
169//!# parser
170//!#     .push_new_param_matcher("param2")
171//!#         .set_param_tag(ParamEnum::Param2)
172//!#         .some_param_indices(&[1]);
173//!#  
174//!# let args = parser.parse_env().unwrap();
175//!#  
176//!# // assert_eq!(args.len(), 6); // commented out to allow documentation tests to pass
177//!#  
178//!# for arg in args {
179//!#     match arg {
180//!#         Arg::Binary(properties) => {
181//!#             assert_eq!(properties.arg_index, 0);
182//!#             // assert_eq!(properties.value_text, "binary name"); // commented out to allow documentation tests to pass
183//!#             assert_eq!(properties.env_line_approximate_char_index, 0);
184//!#         },
185//!#         Arg::Option(properties) => {
186//!#             match properties.matcher.option_tag() {
187//!#                 OptionEnum::A => {
188//!#                     // Process option A
189//!#                     assert_eq!(properties.matcher.name(), "optionA");
190//!#                     assert_eq!(properties.arg_index, 1);
191//!#                     assert_eq!(properties.option_index, 0);
192//!#                     assert_eq!(properties.code, "a");
193//!#                     assert_eq!(properties.value_text, None);
194//!#                     assert_eq!(properties.env_line_approximate_char_index, 14);
195//!#                 },
196//!#                 OptionEnum::B => {
197//!#                     // Process option B
198//!#                 },
199//!#                 OptionEnum::C => {
200//!#                     // Process option C
201//!#                 },
202//!#             }
203//!#         }
204//!#         Arg::Param(properties) => {
205//!#             match properties.matcher.param_tag() {
206//!#                 ParamEnum::Param1 => {
207//!#                     // Process parameter Param1
208//!#                     assert_eq!(properties.matcher.name(), "param1");
209//!#                     assert_eq!(properties.arg_index, 2);
210//!#                     assert_eq!(properties.param_index, 0);
211//!#                     assert_eq!(properties.value_text, "1st \"Param\"");
212//!#                     assert_eq!(properties.env_line_approximate_char_index, 17);
213//!#                 },
214//!#                 ParamEnum::Param2 => {
215//!#                     // Process parameter Param2
216//!#                 },
217//!#             }
218//!#         }
219//!#     }
220//!# }
221//!```
222//! Use the [parse_env()](Parser::parse_env) function instead of the [parse()](Parser::parse_line) function.
223//!```
224//!# use parmacl::{Parser, Arg, RegexOrText, OptionHasValue};
225//!#
226//!# const LINE: &str = r#""binary name" -a "1st ""Param""" -B optValue "param2" -c "C OptValue""#;
227//!#  
228//!# #[derive(Default)]
229//!# enum OptionEnum {
230//!#     #[default] A,
231//!#     B,
232//!#     C,
233//!# }
234//!# #[derive(Default)]
235//!# enum ParamEnum {
236//!#     #[default] Param1,
237//!#     Param2,
238//!# }
239//!#  
240//!# let mut parser: Parser<OptionEnum, ParamEnum> = Parser::with_env_args_defaults();
241//!#  
242//!# parser
243//!#     .push_new_option_matcher("optionA")
244//!#         .set_option_tag(OptionEnum::A)
245//!#         .some_option_codes(&[RegexOrText::with_text("a")]);
246//!#  
247//!# parser
248//!#     .push_new_option_matcher("optionB")
249//!#         .set_option_tag(OptionEnum::B)
250//!#         .some_option_codes(&[RegexOrText::with_text("b")])
251//!#         .set_option_has_value(OptionHasValue::IfPossible);
252//!#  
253//!# parser
254//!#     .push_new_option_matcher("optionC")
255//!#         .set_option_tag(OptionEnum::C)
256//!#         .some_option_codes(&[RegexOrText::with_text("c")])
257//!#         .set_option_has_value(OptionHasValue::Always);
258//!#  
259//!# parser
260//!#      .push_new_param_matcher("param1")
261//!#          .set_param_tag(ParamEnum::Param1)
262//!#          .some_param_indices(&[0]);
263//!#  
264//!# parser
265//!#     .push_new_param_matcher("param2")
266//!#         .set_param_tag(ParamEnum::Param2)
267//!#         .some_param_indices(&[1]);
268//!#  
269//! let args = parser.parse_env().unwrap();
270//!#  
271//!# // assert_eq!(args.len(), 6); // commented out to allow documentation tests to pass
272//!#  
273//!# for arg in args {
274//!#     match arg {
275//!#         Arg::Binary(properties) => {
276//!#             assert_eq!(properties.arg_index, 0);
277//!#             // assert_eq!(properties.value_text, "binary name"); // commented out to allow documentation tests to pass
278//!#             assert_eq!(properties.env_line_approximate_char_index, 0);
279//!#         },
280//!#         Arg::Option(properties) => {
281//!#             match properties.matcher.option_tag() {
282//!#                 OptionEnum::A => {
283//!#                     // Process option A
284//!#                     assert_eq!(properties.matcher.name(), "optionA");
285//!#                     assert_eq!(properties.arg_index, 1);
286//!#                     assert_eq!(properties.option_index, 0);
287//!#                     assert_eq!(properties.code, "a");
288//!#                     assert_eq!(properties.value_text, None);
289//!#                     assert_eq!(properties.env_line_approximate_char_index, 14);
290//!#                 },
291//!#                 OptionEnum::B => {
292//!#                     // Process option B
293//!#                 },
294//!#                 OptionEnum::C => {
295//!#                     // Process option C
296//!#                 },
297//!#             }
298//!#         }
299//!#         Arg::Param(properties) => {
300//!#             match properties.matcher.param_tag() {
301//!#                 ParamEnum::Param1 => {
302//!#                     // Process parameter Param1
303//!#                     assert_eq!(properties.matcher.name(), "param1");
304//!#                     assert_eq!(properties.arg_index, 2);
305//!#                     assert_eq!(properties.param_index, 0);
306//!#                     assert_eq!(properties.value_text, "1st \"Param\"");
307//!#                     assert_eq!(properties.env_line_approximate_char_index, 17);
308//!#                 },
309//!#                 ParamEnum::Param2 => {
310//!#                     // Process parameter Param2
311//!#                 },
312//!#             }
313//!#         }
314//!#     }
315//!# }
316//!```
317//! Since the shell will already have parsed the command line, and passed the individual arguments to the application, the parser can
318//! only guess the position of each argument in the command line. Use property `env_line_approximate_char_index` instead of `char_index`
319//! in [ParamProperties](ParamProperties) or [OptionProperties](OptionProperties) to get the approximate position of a the argument in
320//! the command line.
321//!```
322//!# use parmacl::{Parser, Arg, RegexOrText, OptionHasValue};
323//!#
324//!# const LINE: &str = r#""binary name" -a "1st ""Param""" -B optValue "param2" -c "C OptValue""#;
325//!#  
326//!# #[derive(Default)]
327//!# enum OptionEnum {
328//!#     #[default] A,
329//!#     B,
330//!#     C,
331//!# }
332//!# #[derive(Default)]
333//!# enum ParamEnum {
334//!#     #[default] Param1,
335//!#     Param2,
336//!# }
337//!#  
338//!# let mut parser: Parser<OptionEnum, ParamEnum> = Parser::with_env_args_defaults();
339//!#  
340//!# parser
341//!#     .push_new_option_matcher("optionA")
342//!#         .set_option_tag(OptionEnum::A)
343//!#         .some_option_codes(&[RegexOrText::with_text("a")]);
344//!#  
345//!# parser
346//!#     .push_new_option_matcher("optionB")
347//!#         .set_option_tag(OptionEnum::B)
348//!#         .some_option_codes(&[RegexOrText::with_text("b")])
349//!#         .set_option_has_value(OptionHasValue::IfPossible);
350//!#  
351//!# parser
352//!#     .push_new_option_matcher("optionC")
353//!#         .set_option_tag(OptionEnum::C)
354//!#         .some_option_codes(&[RegexOrText::with_text("c")])
355//!#         .set_option_has_value(OptionHasValue::Always);
356//!#  
357//!# parser
358//!#      .push_new_param_matcher("param1")
359//!#          .set_param_tag(ParamEnum::Param1)
360//!#          .some_param_indices(&[0]);
361//!#  
362//!# parser
363//!#     .push_new_param_matcher("param2")
364//!#         .set_param_tag(ParamEnum::Param2)
365//!#         .some_param_indices(&[1]);
366//!#  
367//!# let args = parser.parse_env().unwrap();
368//!#  
369//!# // assert_eq!(args.len(), 6); // commented out to allow documentation tests to pass
370//!#  
371//!# for arg in args {
372//!#     match arg {
373//!#         Arg::Binary(properties) => {
374//!#             assert_eq!(properties.arg_index, 0);
375//!#             // assert_eq!(properties.value_text, "binary name"); // commented out to allow documentation tests to pass
376//!#             assert_eq!(properties.env_line_approximate_char_index, 0);
377//!#         },
378//!#         Arg::Option(properties) => {
379//!#             match properties.matcher.option_tag() {
380//!                 OptionEnum::A => {
381//!                     // Process option A
382//!                     assert_eq!(properties.matcher.name(), "optionA");
383//!                     assert_eq!(properties.arg_index, 1);
384//!                     assert_eq!(properties.option_index, 0);
385//!                     assert_eq!(properties.code, "a");
386//!                     assert_eq!(properties.value_text, None);
387//!                     assert_eq!(properties.env_line_approximate_char_index, 14);
388//!                 },
389//!#                 OptionEnum::B => {
390//!#                     // Process option B
391//!#                 },
392//!#                 OptionEnum::C => {
393//!#                     // Process option C
394//!#                 },
395//!#             }
396//!#         }
397//!#         Arg::Param(properties) => {
398//!#             match properties.matcher.param_tag() {
399//!#                 ParamEnum::Param1 => {
400//!#                     // Process parameter Param1
401//!#                     assert_eq!(properties.matcher.name(), "param1");
402//!#                     assert_eq!(properties.arg_index, 2);
403//!#                     assert_eq!(properties.param_index, 0);
404//!#                     assert_eq!(properties.value_text, "1st \"Param\"");
405//!#                     assert_eq!(properties.env_line_approximate_char_index, 17);
406//!#                 },
407//!#                 ParamEnum::Param2 => {
408//!#                     // Process parameter Param2
409//!#                 },
410//!#             }
411//!#         }
412//!#     }
413//!# }
414//!```
415//! # Understanding the command line
416//! 
417//! Parmacl considers a command line to have 3 types of arguments
418//! * **Binary name**\
419//! This normally is the first argument in the command line and is normally the path to the application's executable file (binary).
420//! * **Parameters**\
421//! Strings which the application will interpret. Parameters are typically identified by their order in the command line. In the above
422//! [example](#example), `"1st ""Param"""` and `"param2"` are parameters.
423//! * **Options**\
424//! An option is an argument identified by a code.  As such it can be placed anywhere in the command line. It can optionally have a value.
425//! If it does not have a value, it behaves like a flag/boolean.  In the above [example](#example), `-a` is an option with code `a` that
426//! behaves like a flag. The options `-B optValue` (code `B`) and `-c "C OptValue"` (code `c`) are options with respective values
427//! `optValue` and `C OptValue`.
428//! 
429//! Note that these arguments do not necessarily correspond to environment arguments created by a shell and passed to an application.
430//! For example, an option with a value is identified by Parmacl as one argument whereas the shell may identify it as 2 arguments
431//! (depending on what character is used to separate option values from the option code).
432//! 
433//! # Overview of parsing
434//! 
435//! Before parsing a command line, the parser needs to be configured with the style of the command line.  This includes things like
436//! specifying whether parameters and option values can be quoted, whether quotes can be included in quoted parameters and option values,
437//! specifying escaping of special characters.
438//! 
439//! It also needs to be configured with a list of [matchers](Matcher). A matcher is a struct with a set of filters. The filters are used
440//! to match arguments identified by the parser. An argument needs to meet all filter conditions in order for it to be 'matched'
441//!
442//! When a command line is parsed, the parser will identify arguments based on its configured style.  The arguments are identified in order
443//! from start to end of the command line.  As each argument is identified, it is matched against one of the [matchers](Matcher) assigned to
444//! the parser. It is possible for an argument to match more than one matcher.  The parser will attempt to match each argument to matchers
445//! in order of matchers in the parser's matcher list. Accordingly, more specific matchers should be inserted earlier in this list.
446//! 
447//! When an argument is matched, the parser generates a corresponding [Arg](Arg) variant with details of the argument. For parameter and
448//! option arguments, the variant is also assigned a copy of either the respective Matcher's [param_tag](Matcher::param_tag) or
449//! [option_tag](Matcher::option_tag) value.
450//! 
451//! These [Arg](Arg) variants are stored in an array (in same order as the arguments in the command line) which is returned as the success
452//! result.
453//! 
454//! All arguments must be matched. If an argument is not matched, then the user specified an unsupported parameter or option and an
455//! [unmatched parameter](ParseErrorTypeId::UnmatchedParam) or [unmatched option](ParseErrorTypeId::UnmatchedOption) will be returned.
456//! 
457//! If the parser detects an error in the command line, an error result will be returned containing a [ParseError](ParseError) struct.
458//! This struct [identifies](ParseErrorTypeId) the reason for the error and where in the line the error occurred.
459//! 
460//! # Main types
461//! 
462//! * [Parser](Parser)\
463//! The main object. To parse a command line, create an instance of this, set its properties to reflect the style of the command line,
464//! assign matchers and then call one its parse functions. The result will either be the array of arguments or an error object.
465//! * [Matcher](Matcher)\
466//! Each argument must be matched against a matcher.  Typically one matcher is created for each argument however matchers can also
467//! be used to match multiple arguments.
468//! * [Arg](Arg)\
469//! An enum with 3 variants: Binary, Param, Option. The Parser's parse functions return an array of these variants - each of which
470//! identify an argument the parser found in the command line.
471//! * [ArgProperties](ArgProperties)\
472//! A trait shared by structs [BinaryProperties](BinaryProperties), [ParamProperties](ParamProperties) and
473//! [OptionProperties](OptionProperties). Instances of these structs are associated with the respective [Arg](Arg) variants returned
474//! by the parse function and provide details about each identified argument.
475//! * [RegexOrText](RegexOrText)\
476//! A struct representing either a Regex or text (string). An instance of RegexOrText can be assigned to the
477//! [option_codes](Matcher::option_codes) or [value_text](Matcher::value_text) Matcher filter properties and determines whether the
478//! filtering is by text or Regex.
479//! * [ParseError](ParseError)\
480//! The struct returned with an Error result from a parse function. Specifies the type of error and where in the line the error
481//! occurred.
482//! 
483//! # Features
484//! 
485//! * Command line parsing
486//! * Environment arguments parsing
487//! * Command Line Style
488//!     * Specify which character(s) can be used to quote parameters and option values
489//!     * Specify which character(s) can be used to announce an option
490//!     * Specify which character(s) can be used to announce an option value (space character can be included)
491//!     * Specify which character(s) will terminate parsing of a command line
492//!     * Case sensitivity when matching parameters, option codes and option values
493//!     * Whether options with code that have more than one character, require 2 announcer characters (eg --anOpt)
494//!     * Use double quotes to embed quote characters within quoted parameters and option values
495//!     * Use escaping to include characters with special meaning
496//!     * Whether first argument in command line is the binary's name/path
497//! * Argument Matching
498//!     * Parameter or Option
499//!     * Argument indices
500//!     * Parameter indices
501//!     * Parameter text (string or Regex)
502//!     * Option indices
503//!     * Option codes (string or Regex)
504//!     * Whether option has value (None, IfPossible, Always)
505//!     * Option value text (string or Regex)
506//!     * Whether option value can start with an option announcer character
507//! * Tag parameters and options arguments with with any enum (or any other type) from matcher for easy identification
508//! * Parse error result has properties detailing the type of error and where it occurred.
509
510
511
512#![allow(clippy::collapsible_else_if)]
513
514mod env_char;
515mod parse_error_type_id;
516mod parse_error;
517mod regex_or_text;
518mod matcher;
519mod arg;
520mod parser;
521
522mod parse_state;
523
524pub use parse_error_type_id::{
525    ParseErrorTypeId,
526};
527
528pub use parse_error::{
529    ParseError,
530};
531
532pub use regex_or_text::{
533    RegexOrText,
534};
535
536pub use matcher:: {
537    Matcher,
538    Matchers,
539    DefaultTagType,
540    OptionHasValue,
541    MatchArgTypeId,
542    DEFAULT_OPTION_HAS_VALUE,
543};
544
545pub use arg::{
546    ArgProperties,
547    BinaryProperties,
548    OptionProperties,
549    ParamProperties,
550    Arg,
551    Args,
552};
553
554pub use parser::{
555    Parser,
556    EscapeableLogicalChar,
557    DEFAULT_LINE_QUOTE_CHARS,
558    DEFAULT_LINE_OPTION_ANNOUNCER_CHARS,
559    DEFAULT_LINE_OPTION_CODES_CASE_SENSITIVE,
560    DEFAULT_LINE_MULTI_CHAR_OPTION_CODE_REQUIRES_DOUBLE_ANNOUNCER,
561    DEFAULT_LINE_OPTION_VALUE_ANNOUNCER_CHARS,
562    DEFAULT_LINE_OPTION_VALUES_CASE_SENSITIVE,
563    DEFAULT_LINE_PARAMS_CASE_SENSITIVE,
564    DEFAULT_LINE_EMBED_QUOTE_CHAR_WITH_DOUBLE,
565    DEFAULT_LINE_ESCAPE_CHAR,
566    DEFAULT_LINE_ESCAPEABLE_LOGICAL_CHARS,
567    DEFAULT_LINE_ESCAPEABLE_CHARS,
568    DEFAULT_LINE_PARSE_TERMINATE_CHARS,
569    DEFAULT_LINE_FIRST_ARG_IS_BINARY,
570    DEFAULT_ENV_ARGS_QUOTE_CHARS,
571    DEFAULT_ENV_ARGS_OPTION_ANNOUNCER_CHARS,
572    DEFAULT_ENV_ARGS_OPTION_CODES_CASE_SENSITIVE,
573    DEFAULT_ENV_ARGS_OPTION_CODE_CAN_BE_EMPTY,
574    DEFAULT_ENV_ARGS_MULTI_CHAR_OPTION_CODE_REQUIRES_DOUBLE_ANNOUNCER,
575    DEFAULT_ENV_ARGS_OPTION_VALUE_ANNOUNCER_CHARS,
576    DEFAULT_ENV_ARGS_OPTION_VALUES_CASE_SENSITIVE,
577    DEFAULT_ENV_ARGS_PARAMS_CASE_SENSITIVE,
578    DEFAULT_ENV_ARGS_EMBED_QUOTE_CHAR_WITH_DOUBLE,
579    DEFAULT_ENV_ARGS_ESCAPE_CHAR,
580    DEFAULT_ENV_ARGS_ESCAPEABLE_LOGICAL_CHARS,
581    DEFAULT_ENV_ARGS_ESCAPEABLE_CHARS,
582    DEFAULT_ENV_ARGS_PARSE_TERMINATE_CHARS,
583    DEFAULT_ENV_ARGS_FIRST_ARG_IS_BINARY,
584};