xref: /aosp_15_r20/external/bcc/libbpf-tools/android/argp.cpp (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "argp.h"
18 
19 #include <getopt.h>
20 #include <stdio.h>
21 #include <sysexits.h>
22 
23 #include <algorithm>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 
argp_parse(const struct argp * argp,int argc,char ** argv,int,void *,void * input)28 extern "C" error_t argp_parse(const struct argp *argp, int argc, char **argv, int /*unused*/,
29                               void * /* unused */, void *input) {
30     int longindex;
31     std::string optstring;
32     std::vector<struct option> optvec;
33 
34     // process argp_option array for use with getopt_long
35     for (const struct argp_option *opt = argp->options; opt->name || opt->docstring; ++opt) {
36         if (opt->key && isprint(opt->key)) {
37             optstring += opt->key;
38             if (opt->argname) optstring += ':';
39         }
40 
41         if (opt->name) {
42             optvec.push_back({ .name = opt->name, .has_arg = opt->argname ? 1 : 0,
43                     .flag = opt->key ? nullptr : &longindex, .val = opt->key });
44         }
45     }
46     int longhelp = 0;
47     optvec.push_back({ .name = "help", .has_arg = 0, .flag = &longhelp, .val = 1 });
48     optvec.push_back({});
49 
50     int opt;
51     while ((opt = getopt_long(argc, argv, optstring.c_str(), optvec.data(), &longindex)) != -1) {
52         struct argp_state state = { .input = input, .argp = argp };
53         if (!opt) {
54             if (longhelp) argp_state_help(&state, stdout, ARGP_HELP_STD_HELP);
55             return EINVAL;
56         }
57         error_t ret = argp->parser(opt, optarg, &state);
58         if (ret) return ret;
59     }
60 
61     // Handle positional arguments
62     if (optind < argc) {
63         for (int idx = optind; idx < argc; idx++) {
64             struct argp_state state = { .input = input, .argp = argp, .arg_num = idx - optind };
65             const error_t ret = argp->parser(ARGP_KEY_ARG, argv[idx], &state);
66             if (ret) return ret;
67         }
68     }
69     struct argp_state state = {.input = input, .argp = argp};
70     const error_t ret = argp->parser(ARGP_KEY_END, 0, &state);
71     // Not all tools expect ARGP_KEY_END, so ARGP_ERR_UNKNOWN here is benign
72     if (ret && ret != ARGP_ERR_UNKNOWN) return ret;
73     return 0;
74 }
75 
76 
argp_usage(struct argp_state * state)77 extern "C" void argp_usage(struct argp_state* state) {
78     fprintf(stderr, "%s", state->argp->doc);
79     exit(EX_USAGE);
80 }
81 
argp_state_help(struct argp_state * state,FILE * fd,int)82 extern "C" void argp_state_help(struct argp_state* state, FILE *fd, int /* unused */) {
83     constexpr size_t kFlagOffset = 2, kNameOffset = 6, kDocstringOffset = 29;
84 
85     fprintf(fd, "%s\n", state->argp->doc);
86     for (const struct argp_option *opt = state->argp->options; opt->name || opt->docstring; ++opt) {
87         // Skip hidden arguments and empty entries in the argp_option array
88         if (opt->n == OPTION_HIDDEN || (opt->docstring && opt->docstring[0] == '\0')) continue;
89 
90         std::string s(kFlagOffset, ' ');
91 
92         // Append short argument form (e.g. "-p,") if applicable, then whitespace
93         if (opt->key && isprint(opt->key)) {
94             s.append("-");
95             s.append(1, (char)opt->key);
96             s.append(",");
97         }
98         s.append(kNameOffset - s.length(), ' ');
99 
100         // Append long argument form (e.g. "--pid=PID") or whitespace
101         if (opt->name) {
102             s.append("--");
103             s.append(opt->name);
104             if (opt->argname) {
105                 s.append("=");
106                 s.append(opt->argname);
107             }
108         }
109         if (s.length() < kDocstringOffset) {
110             s.append(kDocstringOffset - s.length(), ' ');
111         } else {
112             s.append(" ");
113         }
114 
115         // Append docstring
116         s.append(opt->docstring);
117         s.append("\n");
118         fprintf(fd, "%s", s.c_str());
119     }
120     exit(EX_OK);
121 }
122