Branch data Line data Source code
1 : : #include "precizer.h"
2 : : #include <sysexits.h>
3 : :
4 : : /**
5 : : * @brief Collect the config path and detect other arguments without applying settings
6 : : *
7 : : * The shared option table lets argp distinguish options from their values.
8 : : * Lifecycle events do not count as arguments. Information requests are recorded
9 : : * for source selection; the main parser handles their output. A nonzero quoted
10 : : * value rejects the invocation silently so no file is read and the main parser reports it
11 : : *
12 : : * @param key Option key or argp event
13 : : * @param arg Option value or positional argument
14 : : * @param state Preliminary parser state with ArgumentParseContext in input
15 : : * @return EX_OK on success, EX_USAGE for argp-marked --, or ARGP_ERR_UNKNOWN for bulk operands
16 : : */
17 : 8419 : error_t parse_source_option(
18 : : int key,
19 : : char *arg,
20 : : struct argp_state *state)
21 : : {
22 : 8419 : ArgumentParseContext *context = state->input;
23 : :
24 [ + + + + : 8419 : switch(key)
+ - + ]
25 : : {
26 : 369 : case 'g':
27 : : /* Repeated selections replace the path; no file has been opened */
28 : 369 : config->config_file_path = arg;
29 : 369 : break;
30 : 151 : case 'h':
31 : : case '?':
32 : : case 'z':
33 : : case 'V':
34 : : /* Information requests bypass file selection; output stays in the main pass */
35 : 151 : context->information_mode_requested = true;
36 : 151 : context->has_other_arguments = true;
37 : 151 : break;
38 : 1128 : case ARGP_KEY_END:
39 : : /* Rely on argp's marker without distinguishing option values from delimiters */
40 [ + + ]: 1128 : if(state->quoted != 0)
41 : : {
42 : 34 : return(EX_USAGE);
43 : : }
44 : 1094 : break;
45 : 1094 : case ARGP_KEY_SUCCESS:
46 : : /* Argp has initialized the diagnostic name and stream by this event.
47 : : Retain only these fields: the name refers to argv storage, and the
48 : : stream remains open after this pass. The parser state itself expires
49 : : when argp_parse() returns and must not be retained */
50 : 1094 : context->diagnostic_state.name = state->name;
51 : 1094 : context->diagnostic_state.err_stream = state->err_stream;
52 : 1094 : break;
53 : 2822 : case ARGP_KEY_INIT:
54 : : case ARGP_KEY_NO_ARGS:
55 : : case ARGP_KEY_ERROR:
56 : : case ARGP_KEY_FINI:
57 : 2822 : break;
58 : 0 : case ARGP_KEY_ARGS:
59 : 0 : return(ARGP_ERR_UNKNOWN);
60 : 2855 : default:
61 : : /* Every other option and each positional operand selects CLI settings */
62 : 2855 : context->has_other_arguments = true;
63 : 2855 : break;
64 : : }
65 : :
66 : 8385 : return(EX_OK);
67 : : }
68 : :
69 : : /**
70 : : * @brief Select a configuration file or CLI-only parsing without opening files
71 : : *
72 : : * Config supplies the last explicit config path; the context records whether
73 : : * other CLI arguments were found. Information requests discard the path and use
74 : : * CLI only. Otherwise, an explicit path conflicts with any other arguments.
75 : : * Without an explicit path, only an argument-free invocation selects the
76 : : * optional default file. A preliminary parse error discards partial results
77 : : * silently so the main CLI parser can report the error.
78 : : * Paths are borrowed from argv or static storage; this function does not copy
79 : : * or validate them. An empty explicit path still selects a required file
80 : : *
81 : : * @param argc Original CLI argument count, including the program name
82 : : * @param preliminary_error Return code from preliminary argp parsing
83 : : * @param[in,out] context Preliminary flags; receives whether the selected file is required
84 : : * @return SUCCESS for a selected source or deferred CLI error, FAILURE for a source conflict
85 : : */
86 : 1174 : Return select_settings_source(
87 : : const int argc,
88 : : error_t preliminary_error,
89 : : ArgumentParseContext *context)
90 : : {
91 : : /* Status returned by this function through deliver()
92 : : Default value assumes successful completion */
93 : 1174 : Return status = SUCCESS;
94 : :
95 : : /* Start with no requirement for a file to exist. Only an explicit config
96 : : path accepted without conflicting arguments makes the file required */
97 : 1174 : context->config_required = false;
98 : :
99 [ + + ]: 1174 : if(preliminary_error != EX_OK)
100 : : {
101 : : /* Preliminary parsing may have collected only part of the arguments.
102 : : Discard those results so no file is selected. Leave SUCCESS and print
103 : : nothing here; the main CLI parser must diagnose the original arguments */
104 : 69 : config->config_file_path = NULL;
105 : 69 : context->has_other_arguments = false;
106 : 69 : context->information_mode_requested = false;
107 : :
108 [ + + ]: 1105 : } else if(context->information_mode_requested){
109 : : /* Ignore even an explicit file and leave information output and CLI
110 : : value validation to the main parser */
111 : 136 : config->config_file_path = NULL;
112 : :
113 [ + + ]: 969 : } else if(config->config_file_path != NULL){
114 : :
115 : : /* A non-NULL path records an explicit --config or -g selection.
116 : : Even an empty string counts as explicit; the reader handles opening */
117 [ + + ]: 286 : if(context->has_other_arguments)
118 : : {
119 : : /* An explicit file cannot be combined with other CLI arguments.
120 : : Report the conflict and clear the path before any file is opened.
121 : : FAILURE tells the caller to stop instead of applying either source */
122 : 115 : argp_failure(&context->diagnostic_state,0,0,"ERROR: --config cannot be combined with other arguments. Use either a config file or CLI arguments");
123 : 115 : config->config_file_path = NULL;
124 : 115 : status = FAILURE;
125 : :
126 : : } else {
127 : : /* Keep the explicit path as the sole settings source. Its absence
128 : : must be reported as an error when the reader attempts to open it */
129 : 171 : context->config_required = true;
130 : : }
131 : :
132 [ + + ]: 683 : } else if(argc == 1){
133 : : /* argc includes the program name, so 1 means no CLI arguments.
134 : : Select the default file in the working directory and keep it optional */
135 : 19 : config->config_file_path = APP_NAME ".conf";
136 : : }
137 : :
138 : : /* With other CLI arguments and no explicit config path, the path stays
139 : : NULL and only CLI settings apply. Return the source-selection status */
140 : 1174 : deliver(status);
141 : : }
|