xref: /aosp_15_r20/external/grpc-grpc/test/core/util/cmdline.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "test/core/util/cmdline.h"
20 
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <algorithm>
27 #include <vector>
28 
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_format.h"
31 #include "absl/strings/str_join.h"
32 
33 #include <grpc/support/alloc.h>
34 #include <grpc/support/log.h>
35 
36 #include "src/core/lib/gprpp/memory.h"
37 
38 typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype;
39 
40 typedef struct arg {
41   const char* name;
42   const char* help;
43   argtype type;
44   void* value;
45   struct arg* next;
46 } arg;
47 
48 struct gpr_cmdline {
49   const char* description;
50   arg* args;
51   const char* argv0;
52 
53   const char* extra_arg_name;
54   const char* extra_arg_help;
55   void (*extra_arg)(void* user_data, const char* arg);
56   void* extra_arg_user_data;
57 
58   int (*state)(gpr_cmdline* cl, char* arg);
59   arg* cur_arg;
60 
61   int survive_failure;
62 };
63 
64 static int normal_state(gpr_cmdline* cl, char* str);
65 
gpr_cmdline_create(const char * description)66 gpr_cmdline* gpr_cmdline_create(const char* description) {
67   gpr_cmdline* cl = grpc_core::Zalloc<gpr_cmdline>();
68 
69   cl->description = description;
70   cl->state = normal_state;
71 
72   return cl;
73 }
74 
gpr_cmdline_set_survive_failure(gpr_cmdline * cl)75 void gpr_cmdline_set_survive_failure(gpr_cmdline* cl) {
76   cl->survive_failure = 1;
77 }
78 
gpr_cmdline_destroy(gpr_cmdline * cl)79 void gpr_cmdline_destroy(gpr_cmdline* cl) {
80   while (cl->args) {
81     arg* a = cl->args;
82     cl->args = a->next;
83     gpr_free(a);
84   }
85   gpr_free(cl);
86 }
87 
add_arg(gpr_cmdline * cl,const char * name,const char * help,argtype type,void * value)88 static void add_arg(gpr_cmdline* cl, const char* name, const char* help,
89                     argtype type, void* value) {
90   arg* a;
91 
92   for (a = cl->args; a; a = a->next) {
93     GPR_ASSERT(0 != strcmp(a->name, name));
94   }
95 
96   a = static_cast<arg*>(gpr_zalloc(sizeof(arg)));
97   a->name = name;
98   a->help = help;
99   a->type = type;
100   a->value = value;
101   a->next = cl->args;
102   cl->args = a;
103 }
104 
gpr_cmdline_add_int(gpr_cmdline * cl,const char * name,const char * help,int * value)105 void gpr_cmdline_add_int(gpr_cmdline* cl, const char* name, const char* help,
106                          int* value) {
107   add_arg(cl, name, help, ARGTYPE_INT, value);
108 }
109 
gpr_cmdline_add_flag(gpr_cmdline * cl,const char * name,const char * help,int * value)110 void gpr_cmdline_add_flag(gpr_cmdline* cl, const char* name, const char* help,
111                           int* value) {
112   add_arg(cl, name, help, ARGTYPE_BOOL, value);
113 }
114 
gpr_cmdline_add_string(gpr_cmdline * cl,const char * name,const char * help,const char ** value)115 void gpr_cmdline_add_string(gpr_cmdline* cl, const char* name, const char* help,
116                             const char** value) {
117   add_arg(cl, name, help, ARGTYPE_STRING, value);
118 }
119 
gpr_cmdline_on_extra_arg(gpr_cmdline * cl,const char * name,const char * help,void (* on_extra_arg)(void * user_data,const char * arg),void * user_data)120 void gpr_cmdline_on_extra_arg(
121     gpr_cmdline* cl, const char* name, const char* help,
122     void (*on_extra_arg)(void* user_data, const char* arg), void* user_data) {
123   GPR_ASSERT(!cl->extra_arg);
124   GPR_ASSERT(on_extra_arg);
125 
126   cl->extra_arg = on_extra_arg;
127   cl->extra_arg_user_data = user_data;
128   cl->extra_arg_name = name;
129   cl->extra_arg_help = help;
130 }
131 
132 // recursively descend argument list, adding the last element
133 // to s first - so that arguments are added in the order they were
134 // added to the list by api calls
add_args_to_usage(arg * a,std::vector<std::string> * s)135 static void add_args_to_usage(arg* a, std::vector<std::string>* s) {
136   if (a == nullptr) return;
137   add_args_to_usage(a->next, s);
138   switch (a->type) {
139     case ARGTYPE_BOOL:
140       s->push_back(absl::StrFormat(" [--%s|--no-%s]", a->name, a->name));
141       break;
142     case ARGTYPE_STRING:
143       s->push_back(absl::StrFormat(" [--%s=string]", a->name));
144       break;
145     case ARGTYPE_INT:
146       s->push_back(absl::StrFormat(" [--%s=int]", a->name));
147       break;
148   }
149 }
150 
gpr_cmdline_usage_string(gpr_cmdline * cl,const char * argv0)151 std::string gpr_cmdline_usage_string(gpr_cmdline* cl, const char* argv0) {
152   const char* name = strrchr(argv0, '/');
153   if (name != nullptr) {
154     name++;
155   } else {
156     name = argv0;
157   }
158 
159   std::vector<std::string> s;
160   s.push_back(absl::StrCat("Usage: ", name));
161   add_args_to_usage(cl->args, &s);
162   if (cl->extra_arg) {
163     s.push_back(absl::StrFormat(" [%s...]", cl->extra_arg_name));
164   }
165   s.push_back("\n");
166   return absl::StrJoin(s, "");
167 }
168 
print_usage_and_die(gpr_cmdline * cl)169 static int print_usage_and_die(gpr_cmdline* cl) {
170   fprintf(stderr, "%s", gpr_cmdline_usage_string(cl, cl->argv0).c_str());
171   if (!cl->survive_failure) {
172     exit(1);
173   }
174   return 0;
175 }
176 
extra_state(gpr_cmdline * cl,char * str)177 static int extra_state(gpr_cmdline* cl, char* str) {
178   if (!cl->extra_arg) {
179     return print_usage_and_die(cl);
180   }
181   cl->extra_arg(cl->extra_arg_user_data, str);
182   return 1;
183 }
184 
find_arg(gpr_cmdline * cl,char * name)185 static arg* find_arg(gpr_cmdline* cl, char* name) {
186   arg* a;
187 
188   for (a = cl->args; a; a = a->next) {
189     if (0 == strcmp(a->name, name)) {
190       break;
191     }
192   }
193 
194   if (!a) {
195     fprintf(stderr, "Unknown argument: %s\n", name);
196     return nullptr;
197   }
198 
199   return a;
200 }
201 
value_state(gpr_cmdline * cl,char * str)202 static int value_state(gpr_cmdline* cl, char* str) {
203   long intval;
204   char* end;
205 
206   GPR_ASSERT(cl->cur_arg);
207 
208   switch (cl->cur_arg->type) {
209     case ARGTYPE_INT:
210       intval = strtol(str, &end, 0);
211       if (*end || intval < INT_MIN || intval > INT_MAX) {
212         fprintf(stderr, "expected integer, got '%s' for %s\n", str,
213                 cl->cur_arg->name);
214         return print_usage_and_die(cl);
215       }
216       *static_cast<int*>(cl->cur_arg->value) = static_cast<int>(intval);
217       break;
218     case ARGTYPE_BOOL:
219       if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) {
220         *static_cast<int*>(cl->cur_arg->value) = 1;
221       } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) {
222         *static_cast<int*>(cl->cur_arg->value) = 0;
223       } else {
224         fprintf(stderr, "expected boolean, got '%s' for %s\n", str,
225                 cl->cur_arg->name);
226         return print_usage_and_die(cl);
227       }
228       break;
229     case ARGTYPE_STRING:
230       *static_cast<char**>(cl->cur_arg->value) = str;
231       break;
232   }
233 
234   cl->state = normal_state;
235   return 1;
236 }
237 
normal_state(gpr_cmdline * cl,char * str)238 static int normal_state(gpr_cmdline* cl, char* str) {
239   char* eq = nullptr;
240   char* tmp = nullptr;
241   char* arg_name = nullptr;
242   int r = 1;
243 
244   if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") ||
245       0 == strcmp(str, "-h")) {
246     return print_usage_and_die(cl);
247   }
248 
249   cl->cur_arg = nullptr;
250 
251   if (str[0] == '-') {
252     if (str[1] == '-') {
253       if (str[2] == 0) {
254         // handle '--' to move to just extra args
255         cl->state = extra_state;
256         return 1;
257       }
258       str += 2;
259     } else {
260       str += 1;
261     }
262     // first byte of str is now past the leading '-' or '--'
263     if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') {
264       // str is of the form '--no-foo' - it's a flag disable
265       str += 3;
266       cl->cur_arg = find_arg(cl, str);
267       if (cl->cur_arg == nullptr) {
268         return print_usage_and_die(cl);
269       }
270       if (cl->cur_arg->type != ARGTYPE_BOOL) {
271         fprintf(stderr, "%s is not a flag argument\n", str);
272         return print_usage_and_die(cl);
273       }
274       *static_cast<int*>(cl->cur_arg->value) = 0;
275       return 1;  // early out
276     }
277     eq = strchr(str, '=');
278     if (eq != nullptr) {
279       // copy the string into a temp buffer and extract the name
280       tmp = arg_name =
281           static_cast<char*>(gpr_malloc(static_cast<size_t>(eq - str + 1)));
282       memcpy(arg_name, str, static_cast<size_t>(eq - str));
283       arg_name[eq - str] = 0;
284     } else {
285       arg_name = str;
286     }
287     cl->cur_arg = find_arg(cl, arg_name);
288     if (cl->cur_arg == nullptr) {
289       return print_usage_and_die(cl);
290     }
291     if (eq != nullptr) {
292       // str was of the type --foo=value, parse the value
293       r = value_state(cl, eq + 1);
294     } else if (cl->cur_arg->type != ARGTYPE_BOOL) {
295       // flag types don't have a '--foo value' variant, other types do
296       cl->state = value_state;
297     } else {
298       // flag parameter: just set the value
299       *static_cast<int*>(cl->cur_arg->value) = 1;
300     }
301   } else {
302     r = extra_state(cl, str);
303   }
304 
305   gpr_free(tmp);
306   return r;
307 }
308 
gpr_cmdline_parse(gpr_cmdline * cl,int argc,char ** argv)309 int gpr_cmdline_parse(gpr_cmdline* cl, int argc, char** argv) {
310   int i;
311 
312   GPR_ASSERT(argc >= 1);
313   cl->argv0 = argv[0];
314 
315   for (i = 1; i < argc; i++) {
316     if (!cl->state(cl, argv[i])) {
317       return 0;
318     }
319   }
320   return 1;
321 }
322