GVM User Suite
User tools for the GVM open source project.
cargs.c
Go to the documentation of this file.
1 /*
2 MIT License
3 
4 Copyright (c) 2022 Leonard IklĂ©
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23  */
24 #include <assert.h>
25 #include <cargs.h>
26 #include <memory.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #define CAG_OPTION_PRINT_DISTANCE 4
31 #define CAG_OPTION_PRINT_MIN_INDENTION 20
32 
33 static void cag_option_print_value(const cag_option *option,
34  size_t *accessor_length, FILE *destination)
35 {
36  if (option->value_name != NULL) {
37  *accessor_length += fprintf(destination, "=%s", option->value_name);
38  }
39 }
40 
41 static void cag_option_print_letters(const cag_option *option, bool *first,
42  size_t *accessor_length, FILE *destination)
43 {
44  const char *access_letter;
45  access_letter = option->access_letters;
46  if (access_letter != NULL) {
47  while (*access_letter) {
48  if (*first) {
49  *accessor_length += fprintf(destination, "-%c", *access_letter);
50  *first = false;
51  } else {
52  *accessor_length += fprintf(destination, ", -%c", *access_letter);
53  }
54  ++access_letter;
55  }
56  }
57 }
58 
59 static void cag_option_print_name(const cag_option *option, bool *first,
60  size_t *accessor_length, FILE *destination)
61 {
62  if (option->access_name != NULL) {
63  if (*first) {
64  *accessor_length += fprintf(destination, "--%s", option->access_name);
65  } else {
66  *accessor_length += fprintf(destination, ", --%s", option->access_name);
67  }
68  }
69 }
70 
72  size_t option_count)
73 {
74  size_t option_index, indention, result;
75  const cag_option *option;
76 
78 
79  for (option_index = 0; option_index < option_count; ++option_index) {
80  indention = CAG_OPTION_PRINT_DISTANCE;
81  option = &options[option_index];
82  if (option->access_letters != NULL && *option->access_letters) {
83  indention += strlen(option->access_letters) * 4 - 2;
84  if (option->access_name != NULL) {
85  indention += strlen(option->access_name) + 4;
86  }
87  } else if (option->access_name != NULL) {
88  indention += strlen(option->access_name) + 2;
89  }
90 
91  if (option->value_name != NULL) {
92  indention += strlen(option->value_name) + 1;
93  }
94 
95  if (indention > result) {
96  result = indention;
97  }
98  }
99 
100  return result;
101 }
102 
103 void cag_option_print(const cag_option *options, size_t option_count,
104  FILE *destination)
105 {
106  size_t option_index, indention, i, accessor_length;
107  const cag_option *option;
108  bool first;
109 
110  indention = cag_option_get_print_indention(options, option_count);
111 
112  for (option_index = 0; option_index < option_count; ++option_index) {
113  option = &options[option_index];
114  accessor_length = 0;
115  first = true;
116 
117  fputs(" ", destination);
118 
119  cag_option_print_letters(option, &first, &accessor_length, destination);
120  cag_option_print_name(option, &first, &accessor_length, destination);
121  cag_option_print_value(option, &accessor_length, destination);
122 
123  for (i = accessor_length; i < indention; ++i) {
124  fputs(" ", destination);
125  }
126 
127  fputs(" ", destination);
128  fputs(option->description, destination);
129 
130  fprintf(destination, "\n");
131  }
132 }
133 
135  size_t option_count, int argc, char **argv)
136 {
137  // This just initialized the values to the beginning of all the arguments.
138  context->options = options;
139  context->option_count = option_count;
140  context->argc = argc;
141  context->argv = argv;
142  context->index = 1;
143  context->inner_index = 0;
144  context->forced_end = false;
145 }
146 
148  char *name, size_t name_size)
149 {
150  const cag_option *option;
151  size_t i;
152 
153  // We loop over all the available options and stop as soon as we have found
154  // one. We don't use any hash map table, since there won't be that many
155  // arguments anyway.
156  for (i = 0; i < context->option_count; ++i) {
157  option = &context->options[i];
158 
159  // The option might not have an item name, we can just skip those.
160  if (option->access_name == NULL) {
161  continue;
162  }
163 
164  // Try to compare the name of the access name. We can use the name_size or
165  // this comparison, since we are guaranteed to have null-terminated access
166  // names.
167  if (strncmp(option->access_name, name, name_size) == 0) {
168  return option;
169  }
170  }
171 
172  return NULL;
173 }
174 
176  char letter)
177 {
178  const cag_option *option;
179  size_t i;
180 
181  // We loop over all the available options and stop as soon as we have found
182  // one. We don't use any look up table, since there won't be that many
183  // arguments anyway.
184  for (i = 0; i < context->option_count; ++i) {
185  option = &context->options[i];
186 
187  // If this option doesn't have any access letters we will skip them.
188  if (option->access_letters == NULL) {
189  continue;
190  }
191 
192  // Verify whether this option has the access letter in it's access letter
193  // string. If it does, then this is our option.
194  if (strchr(option->access_letters, letter) != NULL) {
195  return option;
196  }
197  }
198 
199  return NULL;
200 }
201 
203  const cag_option *option, char **c)
204 {
205  // And now let's check whether this option is supposed to have a value, which
206  // is the case if there is a value name set. The value can be either submitted
207  // with a '=' sign or a space, which means we would have to jump over to the
208  // next argv index. This is somewhat ugly, but we do it to behave the same as
209  // the other option parsers.
210  if (option->value_name != NULL) {
211  if (**c == '=') {
212  context->value = ++(*c);
213  } else {
214  // If the next index is larger or equal to the argument count, then the
215  // parameter for this option is missing. The user will know about this,
216  // since the value pointer of the context will be NULL because we don't
217  // set it here in that case.
218  if (context->argc > context->index + 1) {
219  // We consider this argv to be the value, no matter what the contents
220  // are.
221  ++context->index;
222  *c = context->argv[context->index];
223  context->value = *c;
224  }
225  }
226 
227  // Move c to the end of the value, to not confuse the caller about our
228  // position.
229  while (**c) {
230  ++(*c);
231  }
232  }
233 }
234 
235 static void cag_option_parse_access_name(cag_option_context *context, char **c)
236 {
237  const cag_option *option;
238  char *n;
239 
240  // Now we need to extract the access name, which is any symbol up to a '=' or
241  // a '\0'.
242  n = *c;
243  while (**c && **c != '=') {
244  ++*c;
245  }
246 
247  // Now this will obviously always be true, but we are paranoid. Sometimes. It
248  // doesn't hurt to check.
249  assert(*c >= n);
250 
251  // Figure out which option this name belongs to. This might return NULL if the
252  // name is not registered, which means the user supplied an unknown option. In
253  // that case we return true to indicate that we finished with this option. We
254  // have to skip the value parsing since we don't know whether the user thinks
255  // this option has one or not. Since we don't set any identifier specifically,
256  // it will remain '?' within the context.
257  option = cag_option_find_by_name(context, n, (size_t)(*c - n));
258  if (option == NULL) {
259  // Since this option is invalid, we will move on to the next index. There is
260  // nothing we can do about this.
261  ++context->index;
262  return;
263  }
264 
265  // We found an option and now we can specify the identifier within the
266  // context.
267  context->identifier = option->identifier;
268 
269  // And now we try to parse the value. This function will also check whether
270  // this option is actually supposed to have a value.
271  cag_option_parse_value(context, option, c);
272 
273  // And finally we move on to the next index.
274  ++context->index;
275 }
276 
278  char **c)
279 {
280  const cag_option *option;
281  char *n = *c;
282  char *v;
283 
284  // Figure out which option this letter belongs to. This might return NULL if
285  // the letter is not registered, which means the user supplied an unknown
286  // option. In that case we return true to indicate that we finished with this
287  // option. We have to skip the value parsing since we don't know whether the
288  // user thinks this option has one or not. Since we don't set any identifier
289  // specifically, it will remain '?' within the context.
290  option = cag_option_find_by_letter(context, n[context->inner_index]);
291  if (option == NULL) {
292  ++context->index;
293  context->inner_index = 0;
294  return;
295  }
296 
297  // We found an option and now we can specify the identifier within the
298  // context.
299  context->identifier = option->identifier;
300 
301  // And now we try to parse the value. This function will also check whether
302  // this option is actually supposed to have a value.
303  v = &n[++context->inner_index];
304  cag_option_parse_value(context, option, &v);
305 
306  // Check whether we reached the end of this option argument.
307  if (*v == '\0') {
308  ++context->index;
309  context->inner_index = 0;
310  }
311 }
312 
313 static void cag_option_shift(cag_option_context *context, int start, int option,
314  int end)
315 {
316  char *tmp;
317  int a_index, shift_index, shift_count, left_index, right_index;
318 
319  shift_count = option - start;
320 
321  // There is no shift is required if the start and the option have the same
322  // index.
323  if (shift_count == 0) {
324  return;
325  }
326 
327  // Lets loop through the option strings first, which we will move towards the
328  // beginning.
329  for (a_index = option; a_index < end; ++a_index) {
330  // First remember the current option value, because we will have to save
331  // that later at the beginning.
332  tmp = context->argv[a_index];
333 
334  // Let's loop over all option values and shift them one towards the end.
335  // This will override the option value we just stored temporarily.
336  for (shift_index = 0; shift_index < shift_count; ++shift_index) {
337  left_index = a_index - shift_index;
338  right_index = a_index - shift_index - 1;
339  context->argv[left_index] = context->argv[right_index];
340  }
341 
342  // Now restore the saved option value at the beginning.
343  context->argv[a_index - shift_count] = tmp;
344  }
345 
346  // The new index will be before all non-option values, in such a way that they
347  // all will be moved again in the next fetch call.
348  context->index = end - shift_count;
349 }
350 
351 static bool cag_option_is_argument_string(const char *c)
352 {
353  return *c == '-' && *(c + 1) != '\0';
354 }
355 
357 {
358  int next_index, next_option_index;
359  char *c;
360 
361  // Prepare to search the next option at the next index.
362  next_index = context->index;
363  next_option_index = next_index;
364 
365  // Grab a pointer to the string and verify that it is not the end. If it is
366  // the end, we have to return false to indicate that we finished.
367  c = context->argv[next_option_index];
368  if (context->forced_end || c == NULL) {
369  return -1;
370  }
371 
372  // Check whether it is a '-'. We need to find the next option - and an option
373  // always starts with a '-'. If there is a string "-\0", we don't consider it
374  // as an option neither.
375  while (!cag_option_is_argument_string(c)) {
376  c = context->argv[++next_option_index];
377  if (c == NULL) {
378  // We reached the end and did not find any argument anymore. Let's tell
379  // our caller that we reached the end.
380  return -1;
381  }
382  }
383 
384  // Indicate that we found an option which can be processed. The index of the
385  // next option will be returned.
386  return next_option_index;
387 }
388 
390 {
391  char *c;
392  int old_index, new_index;
393 
394  // Reset our identifier to a question mark, which indicates an "unknown"
395  // option. The value is set to NULL, to make sure we are not carrying the
396  // parameter from the previous option to this one.
397  context->identifier = '?';
398  context->value = NULL;
399 
400  // Check whether there are any options left to parse and remember the old
401  // index as well as the new index. In the end we will move the option junk to
402  // the beginning, so that non option arguments can be read.
403  old_index = context->index;
404  new_index = cag_option_find_next(context);
405  if (new_index >= 0) {
406  context->index = new_index;
407  } else {
408  return false;
409  }
410 
411  // Grab a pointer to the beginning of the option. At this point, the next
412  // character must be a '-', since if it was not the prepare function would
413  // have returned false. We will skip that symbol and proceed.
414  c = context->argv[context->index];
415  assert(*c == '-');
416  ++c;
417 
418  // Check whether this is a long option, starting with a double "--".
419  if (*c == '-') {
420  ++c;
421 
422  // This might be a double "--" which indicates the end of options. If this
423  // is the case, we will not move to the next index. That ensures that
424  // another call to the fetch function will not skip the "--".
425  if (*c == '\0') {
426  context->forced_end = true;
427  } else {
428  // We parse now the access name. All information about it will be written
429  // to the context.
430  cag_option_parse_access_name(context, &c);
431  }
432  } else {
433  // This is no long option, so we can just parse an access letter.
434  cag_option_parse_access_letter(context, &c);
435  }
436 
437  // Move the items so that the options come first followed by non-option
438  // arguments.
439  cag_option_shift(context, old_index, new_index, context->index);
440 
441  return context->forced_end == false;
442 }
443 
444 char cag_option_get(const cag_option_context *context)
445 {
446  // We just return the identifier here.
447  return context->identifier;
448 }
449 
450 const char *cag_option_get_value(const cag_option_context *context)
451 {
452  // We just return the internal value pointer of the context.
453  return context->value;
454 }
455 
457 {
458  // Either we point to a value item,
459  return context->index;
460 }
static void cag_option_parse_access_letter(cag_option_context *context, char **c)
Definition: cargs.c:277
static void cag_option_shift(cag_option_context *context, int start, int option, int end)
Definition: cargs.c:313
char cag_option_get(const cag_option_context *context)
Gets the identifier of the option.
Definition: cargs.c:444
void cag_option_print(const cag_option *options, size_t option_count, FILE *destination)
Prints all options to the terminal.
Definition: cargs.c:103
int cag_option_get_index(const cag_option_context *context)
Gets the current index of the context.
Definition: cargs.c:456
static const cag_option * cag_option_find_by_name(cag_option_context *context, char *name, size_t name_size)
Definition: cargs.c:147
const char * cag_option_get_value(const cag_option_context *context)
Gets the value from the option.
Definition: cargs.c:450
static int cag_option_find_next(cag_option_context *context)
Definition: cargs.c:356
static size_t cag_option_get_print_indention(const cag_option *options, size_t option_count)
Definition: cargs.c:71
#define CAG_OPTION_PRINT_DISTANCE
Definition: cargs.c:30
#define CAG_OPTION_PRINT_MIN_INDENTION
Definition: cargs.c:31
void cag_option_prepare(cag_option_context *context, const cag_option *options, size_t option_count, int argc, char **argv)
Prepare argument options context for parsing.
Definition: cargs.c:134
static const cag_option * cag_option_find_by_letter(cag_option_context *context, char letter)
Definition: cargs.c:175
static void cag_option_print_name(const cag_option *option, bool *first, size_t *accessor_length, FILE *destination)
Definition: cargs.c:59
bool cag_option_fetch(cag_option_context *context)
Fetches an option from the argument list.
Definition: cargs.c:389
static void cag_option_print_value(const cag_option *option, size_t *accessor_length, FILE *destination)
Definition: cargs.c:33
static bool cag_option_is_argument_string(const char *c)
Definition: cargs.c:351
static void cag_option_print_letters(const cag_option *option, bool *first, size_t *accessor_length, FILE *destination)
Definition: cargs.c:41
static void cag_option_parse_value(cag_option_context *context, const cag_option *option, char **c)
Definition: cargs.c:202
static void cag_option_parse_access_name(cag_option_context *context, char **c)
Definition: cargs.c:235
static struct cag_option options[]
Definition: gvm-cli.cpp:45
char ** argv
Definition: cargs.h:86
char * value
Definition: cargs.h:91
char identifier
Definition: cargs.h:90
size_t option_count
Definition: cargs.h:84
const struct cag_option * options
Definition: cargs.h:83
bool forced_end
Definition: cargs.h:89
const char * access_name
Definition: cargs.h:72
const char * access_letters
Definition: cargs.h:71
const char * value_name
Definition: cargs.h:73
const char * description
Definition: cargs.h:74
const char identifier
Definition: cargs.h:70