The ROme OpTimistic Simulator  3.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
arg_parse.c
Go to the documentation of this file.
1 
11 #include <core/arg_parse.h>
12 
13 #include <mm/mm.h>
14 
15 #include <ctype.h>
16 #include <stdarg.h>
17 #include <stdio.h>
18 #include <string.h>
19 
21 #define SCREEN_LENGTH 80
22 #define HELP_INDENT 6
24 #define HELP_OPT_LEN_MIN 20
26 #define HELP_OPT_LEN_MAX 26
28 #define HELP_SPACES_MIN 3
30 #define USAGE_INDENT 11
32 
34 static const char *ap_pname;
36 static const struct ap_settings *ap_settings;
37 
46 };
47 
49 static struct ap_option ap_internal_opts[] = {
50  {"help", AP_HELP, NULL, "Give this help list"},
51  {"usage", AP_USAGE, NULL, "Give a short usage message"},
52  {"version", AP_VERSION, NULL, "Print program version"},
53  {0}
54 };
55 
63 static int cmp_opts (const void *a, const void *b)
64 {
65  const struct ap_option *oa = a, *ob = b;
66  return strcmp(oa->name, ob->name);
67 }
68 
76 static int cmp_sects (const void *a, const void *b)
77 {
78  const struct ap_section *oa = a, *ob = b;
79 
80  if (oa->header == NULL || ob->header == NULL) {
81  return (ob->header == NULL) - (oa->header == NULL);
82  }
83 
84  return strcmp(oa->header, ob->header);
85 }
86 
95 static void print_indented_string(const char *str, int curr_i, int indent)
96 {
97  const char *p = str;
98 
99  while (*p) {
100  while (*p && !isspace(*p)) ++p;
101 
102  int l = p - str;
103  if (l + curr_i > SCREEN_LENGTH) {
104  printf("\n%*c", indent, ' ');
105  curr_i = indent;
106  }
107  printf("%.*s", l, str);
108  curr_i += l;
109 
110  while(*p && isspace(*p)) {
111  if (*p == '\n' || curr_i >= SCREEN_LENGTH) {
112  printf("\n%*c", indent, ' ');
113  curr_i = indent;
114  }
115  if (*p != '\n') {
116  putchar(*p);
117  ++curr_i;
118  }
119  ++p;
120  }
121  str = p;
122  }
123 }
124 
131 static void print_help_option(const char *name, const char *arg, const char *doc)
132 {
133  int l = printf("%*c--%s%s%s", HELP_INDENT, ' ',
134  name, arg ? "=" : "", arg ? arg : "");
135 
136  if (l > HELP_OPT_LEN_MAX + HELP_INDENT) {
138  printf("\n%*c", l, ' ');
139  } else if (l < HELP_OPT_LEN_MIN + HELP_INDENT) {
140  l += printf("%*c", HELP_OPT_LEN_MIN + HELP_SPACES_MIN +
141  HELP_INDENT - l, ' ');
142  } else {
143  l += printf("%*c", HELP_SPACES_MIN, ' ');
144  }
145 
146  if(doc)
149 
150  puts("");
151 }
152 
156 static void print_help(void)
157 {
158  printf("Usage: %s [OPTION...]\n\n%s\n", ap_pname, ap_settings->prog_doc);
159 
160  struct ap_section *s = ap_settings->sects;
161 
162  do {
163  struct ap_option *o = s->opts;
164 
165  if (!o->name)
166  continue;
167 
168  if (s->header)
169  printf(" %s\n", s->header);
170 
171  do {
172  print_help_option(o->name, o->arg, o->doc);
173  } while ((++o)->name);
174 
175  puts("");
176  } while ((s++)->opts != ap_internal_opts);
177 
178  printf("Report bugs to %s.\n", ap_settings->prog_report);
179 }
180 
188 static int print_usage_option(const char *name, const char *arg, int curr_i)
189 {
190  int l = snprintf(NULL, 0, " [--%s%s%s]", name,
191  arg ? "=" : "", arg ? arg : "");
192 
193  if (curr_i + l > SCREEN_LENGTH) {
194  printf("\n%*c", USAGE_INDENT, ' ');
195  curr_i = l + USAGE_INDENT;
196  } else {
197  curr_i += l;
198  }
199 
200  printf(" [--%s%s%s]", name, arg ? "=" : "", arg ? arg : "");
201 
202  return curr_i;
203 }
204 
208 static void print_usage(void)
209 {
210  int curr_i = printf("Usage: %s", ap_pname);
211 
212  struct ap_section *s = ap_settings->sects;
213 
214  do {
215  struct ap_option *o = s->opts;
216 
217  while (o->name) {
218  curr_i = print_usage_option(o->name, o->arg, curr_i);
219  ++o;
220  }
221 
222  } while ((s++)->opts != ap_internal_opts);
223 
224  puts("");
225 }
226 
233 static void internal_opt_parse(int key, const char *arg)
234 {
235  (void)arg;
236  switch (key) {
237  case AP_HELP:
238  print_help();
239  break;
240  case AP_USAGE:
241  print_usage();
242  break;
243  case AP_VERSION:
244  puts(ap_settings->prog_version);
245  break;
246  default:
247  return;
248  }
249  exit(0);
250 }
251 
256 static void sort_and_setup_settings(void)
257 {
258  struct ap_section *s = ap_settings->sects;
259  size_t sects_cnt = 0;
260 
261  while (s[sects_cnt].opts) {
262  struct ap_option *o = s[sects_cnt].opts;
263 
264  size_t opts_cnt = 0;
265 
266  while (o[opts_cnt].name) {
267  ++opts_cnt;
268  }
269 
270  qsort(o, opts_cnt, sizeof(struct ap_option), cmp_opts);
271 
272  ++sects_cnt;
273  }
274 
275  qsort(s, sects_cnt, sizeof(struct ap_section), cmp_sects);
276 
277  // injects the internal options to ease the native options handling
278  s[sects_cnt].header = NULL;
279  s[sects_cnt].opts = ap_internal_opts;
280  s[sects_cnt].parser = internal_opt_parse;
281 }
282 
286 static void undo_setup_settings(void)
287 {
288  struct ap_section *s = ap_settings->sects;
289  while (s->opts != ap_internal_opts)
290  ++s;
291  s->opts = NULL;
292  s->parser = NULL;
293 }
294 
306 static int parse_option(struct ap_section *s, struct ap_option *o,
307  const char *arg, bool arg_explicit)
308 {
309  if (!arg && o->arg)
310  arg_parse_error("option '--%s' requires an argument", o->name);
311 
312  if (arg && !o->arg) {
313  if (arg_explicit)
315  "option '--%s' does not require an argument", o->name);
316  else if (arg[0] != '-')
317  arg_parse_error("too many arguments");
318  else
319  arg = NULL;
320  }
321 
322  s->parser(o->key, arg);
323 
324  return arg != NULL && !arg_explicit;
325 }
326 
334 static int process_option(const char *o_name, const char *arg)
335 {
336  unsigned max_s = 0;
337  struct ap_option *cand_o = NULL;
338  struct ap_section *cand_s = NULL;
339  struct ap_section *s = ap_settings->sects;
340 
341  do {
342  struct ap_option *o = s->opts;
343 
344  while (o->name) {
345  unsigned i = 0;
346  while(o_name[i] && o->name[i] == o_name[i])
347  ++i;
348 
349  if (o_name[i] == o->name[i])
350  return parse_option(s, o, arg, false);
351 
352  if (max_s == i)
353  cand_o = NULL;
354 
355  if (max_s < i) {
356  max_s = i;
357  cand_o = o;
358  cand_s = s;
359  }
360 
361  ++o;
362  }
363  } while ((s++)->opts != ap_internal_opts);
364 
365  if (!max_s || (o_name[max_s] && o_name[max_s] != '='))
366  arg_parse_error("unrecognized option '--%s'", o_name);
367 
368  if (!cand_o)
369  arg_parse_error("ambiguous option '--%s'", o_name);
370 
371  if (o_name[max_s] == '=')
372  return parse_option(cand_s, cand_o, &o_name[max_s + 1], true);
373 
374  return parse_option(cand_s, cand_o, arg, false);
375 }
376 
382 void arg_parse_run(struct ap_settings *ap_s, char **argv)
383 {
384  ap_pname = strrchr(*argv, '/');
385  ap_pname = ap_pname ? ap_pname + 1 : *argv;
386 
387  ap_settings = ap_s;
388 
390 
391  struct ap_section *s = ap_s->sects;
392  do {
393  s->parser(AP_KEY_INIT, NULL);
394  } while ((s++)->opts != ap_internal_opts);
395 
396  ++argv;
397  while (*argv) {
398  const char *str = *argv;
399  if (str[0] != '-')
400  arg_parse_error("too many arguments");
401 
402  if (str[1] != '-')
403  arg_parse_error("invalid option -- '%s'", &str[1]);
404 
405  argv += process_option(&str[2], *(argv + 1));
406  ++argv;
407  }
408 
409  s = ap_s->sects;
410  do {
411  s->parser(AP_KEY_FINI, NULL);
412  } while ((s++)->opts != ap_internal_opts);
413 
415 }
416 
422 const char *arg_parse_program_name(void)
423 {
424  return ap_pname;
425 }
426 
432 void arg_parse_error(const char *fmt, ...)
433 {
434  fprintf(stderr, "%s: ", ap_pname);
435 
436  va_list args;
437  va_start(args, fmt);
438  vfprintf(stderr, fmt, args);
439  va_end(args);
440 
441  fprintf(stderr,
442  "\nTry `%s --help' or `%s --usage' for more information.\n",
443  ap_pname, ap_pname);
444 
445  exit(64);
446 }
ap_section::header
const char * header
The header printed before this section in the --help text.
Definition: arg_parse.h:39
AP_VERSION
@ AP_VERSION
Identifies the --version option.
Definition: arg_parse.c:45
print_usage_option
static int print_usage_option(const char *name, const char *arg, int curr_i)
Prints the help text for a single option.
Definition: arg_parse.c:188
ap_pname
static const char * ap_pname
The program name extracted from the command line.
Definition: arg_parse.c:34
ap_option::arg
const char * arg
The argument name for this option, shown in the --usage text.
Definition: arg_parse.h:22
ap_settings::prog_doc
const char * prog_doc
A description of the program.
Definition: arg_parse.h:51
AP_HELP
@ AP_HELP
Identifies the --help option.
Definition: arg_parse.c:41
AP_USAGE
@ AP_USAGE
Identifies the --usage option.
Definition: arg_parse.c:43
ap_option
A single parsable command line option.
Definition: arg_parse.h:14
process_option
static int process_option(const char *o_name, const char *arg)
Parses a single option by calling the appropriate parser or throwing an error.
Definition: arg_parse.c:334
cmp_opts
static int cmp_opts(const void *a, const void *b)
Compares two ap_option structs alphabetically.
Definition: arg_parse.c:63
AP_KEY_INIT
@ AP_KEY_INIT
Signals the start of the parsing process.
Definition: arg_parse.h:31
ap_option::doc
const char * doc
The documentation of this option, shown in the --help text.
Definition: arg_parse.h:25
ap_section
A set of options organized and parsed together.
Definition: arg_parse.h:37
internal_opt_key
internal_opt_key
The keys used in the internal struct ap_option to handle base options.
Definition: arg_parse.c:39
sort_and_setup_settings
static void sort_and_setup_settings(void)
Sets up the ap_section structs and their ap_option structs by sorting them. The internal ap_section s...
Definition: arg_parse.c:256
ap_option::key
int key
The key passed to the parsing function when encountering this option.
Definition: arg_parse.h:19
AP_KEY_FINI
@ AP_KEY_FINI
Signals the end of the parsing process.
Definition: arg_parse.h:33
ap_settings
A complete command line option parsing setup.
Definition: arg_parse.h:49
print_help
static void print_help(void)
Prints the help text for the whole argument parsing context.
Definition: arg_parse.c:156
arg_parse_run
void arg_parse_run(struct ap_settings *ap_s, char **argv)
Parses the command line options.
Definition: arg_parse.c:382
SCREEN_LENGTH
#define SCREEN_LENGTH
Expected length of the terminal expressed in monospace characters width.
Definition: arg_parse.c:21
undo_setup_settings
static void undo_setup_settings(void)
Undos the injection of the internal ap_section struct.
Definition: arg_parse.c:286
print_help_option
static void print_help_option(const char *name, const char *arg, const char *doc)
Prints the help text for a single option.
Definition: arg_parse.c:131
parse_option
static int parse_option(struct ap_section *s, struct ap_option *o, const char *arg, bool arg_explicit)
Parses a single option by calling the appropriate parser or throwing an error.
Definition: arg_parse.c:306
ap_settings::prog_version
const char * prog_version
A description of the program version.
Definition: arg_parse.h:53
ap_internal_opts
static struct ap_option ap_internal_opts[]
The internal struct ap_option used to handle base options.
Definition: arg_parse.c:49
HELP_OPT_LEN_MAX
#define HELP_OPT_LEN_MAX
Maximum indentation of the documentation strings in the --help entries.
Definition: arg_parse.c:27
print_usage
static void print_usage(void)
Prints the usage text for the whole argument parsing context.
Definition: arg_parse.c:208
HELP_OPT_LEN_MIN
#define HELP_OPT_LEN_MIN
Minimum indentation of the documentation strings in the --help entries.
Definition: arg_parse.c:25
print_indented_string
static void print_indented_string(const char *str, int curr_i, int indent)
Prints a string in the terminal indenting it and wrapping it.
Definition: arg_parse.c:95
HELP_SPACES_MIN
#define HELP_SPACES_MIN
Minimum spaces between the option and its documentation in the --help text.
Definition: arg_parse.c:29
arg_parse.h
Command line option parser.
ap_option::name
const char * name
The long option name.
Definition: arg_parse.h:16
ap_settings::prog_report
const char * prog_report
The email address of the maintainer.
Definition: arg_parse.h:55
USAGE_INDENT
#define USAGE_INDENT
Number of spaces to indent the --usage text.
Definition: arg_parse.c:31
ap_settings
static const struct ap_settings * ap_settings
The currently parsed struct ap_settings.
Definition: arg_parse.c:36
cmp_sects
static int cmp_sects(const void *a, const void *b)
Compares two ap_section structs (used to reorder sections alphabetically)
Definition: arg_parse.c:76
arg_parse_program_name
const char * arg_parse_program_name(void)
Gets the program name.
Definition: arg_parse.c:422
arg_parse_error
void arg_parse_error(const char *fmt,...)
Prints a parsing related error message and exits with a bad exit code.
Definition: arg_parse.c:432
ap_settings::sects
struct ap_section * sects
The array of supported sections, terminated by a zeroed element.
Definition: arg_parse.h:57
ap_section::opts
struct ap_option * opts
The array of recognized options, terminated by a zeroed element.
Definition: arg_parse.h:41
internal_opt_parse
static void internal_opt_parse(int key, const char *arg)
The parsing function for the internal options (–help, –usage, –version)
Definition: arg_parse.c:233
HELP_INDENT
#define HELP_INDENT
Number of spaces to indent the --help entries.
Definition: arg_parse.c:23
mm.h
Memory Manager main header.
ap_section::parser
void(* parser)(int key, const char *arg)
The parsing function.
Definition: arg_parse.h:45