1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2023 Pakfire development team #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 #############################################################################*/
30 static const struct command
* command_find(const struct command
* commands
, const char* verb
) {
31 for (const struct command
* command
= commands
; command
->verb
; command
++) {
32 if (strcmp(command
->verb
, verb
) == 0)
39 static int count_arguments(int argc
, char* argv
[]) {
42 for (int i
= 1; i
< argc
; i
++) {
52 static int update_program_name(int argc
, char* argv
[], const char* verb
) {
53 char* program_name
= NULL
;
56 // XXX maybe program_name should move into the ctx?
59 r
= asprintf(&program_name
, "%s %s", program_invocation_short_name
, verb
);
63 program_invocation_short_name
= argv
[0] = program_name
;
68 static int command_run(const struct command
* command
, int argc
, char* argv
[], void* data
) {
74 // Update the program name
75 r
= update_program_name(argc
, argv
, command
->verb
);
80 return command
->callback(data
, argc
, argv
);
84 const struct argp_option
* options
;
85 const struct command
* commands
;
90 // The selected command
91 const struct command
* command
;
96 static error_t
__command_parse(int key
, char* arg
, struct argp_state
* state
) {
97 struct command_ctx
* ctx
= state
->input
;
99 // Just call the parse function if we don't have any commands
100 if (!ctx
->commands
) {
102 return ARGP_ERR_UNKNOWN
;
104 return ctx
->parse(key
, arg
, state
, ctx
->data
);
108 // Show help if no arguments have been passed
109 case ARGP_KEY_NO_ARGS
:
110 argp_failure(state
, EXIT_FAILURE
, 0, "Missing command");
113 // Try to find a command
115 const struct command
* command
= ctx
->command
= command_find(ctx
->commands
, arg
);
117 // Fail if the command wasn't found
119 argp_failure(state
, EXIT_FAILURE
, 0, "Unknown command '%s'", arg
);
123 // XXX actually we should update the program name here
125 // Return UNKNOWN so that we get called for ARGP_KEY_ARGS
126 return ARGP_ERR_UNKNOWN
;
128 // Store all remaining options & arguments
130 ctx
->argc
= state
->argc
- state
->next
;
131 ctx
->argv
= &state
->argv
[state
->next
];
134 // Perform some final checks when parsing has been completed
135 case ARGP_KEY_SUCCESS
:
136 // Check for root privileges
137 if (ctx
->flags
& CLI_REQUIRE_ROOT
) {
138 if (getuid() || getgid())
139 argp_failure(state
, EXIT_FAILURE
, 0, "Must be run as root");
143 int args
= count_arguments(ctx
->argc
, ctx
->argv
);
145 // Check if we have a sufficient number of arguments
146 if (ctx
->command
->min_args
> 0 && args
< ctx
->command
->min_args
)
147 argp_error(state
, "Not enough arguments");
149 else if (ctx
->command
->max_args
>= 0 && args
> ctx
->command
->max_args
)
150 argp_error(state
, "Too many arguments");
154 // Do not pass any other things to the callback
161 // Otherwise call the callback
164 return ARGP_ERR_UNKNOWN
;
166 return ctx
->parse(key
, arg
, state
, ctx
->data
);
172 int cli_parse(const struct argp_option
* options
, const struct command
* commands
,
173 const char* args_doc
, const char* doc
,
174 command_parse parse
, int flags
, int argc
, char** argv
, void* data
) {
178 struct command_ctx ctx
= {
180 .commands
= commands
,
187 struct argp parser
= {
189 .parser
= __command_parse
,
190 .args_doc
= args_doc
,
195 // Parse command line options
196 r
= argp_parse(&parser
, argc
, argv
, ARGP_IN_ORDER
, &arg_index
, &ctx
);
200 // Dispatch the selected command
203 r
= command_run(ctx
.command
, ctx
.argc
, ctx
.argv
, ctx
.data
);