1 /*
2  * Copyright (c) 2016-2018, NVIDIA CORPORATION.  All rights reserved.
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 
18 /** \file
19  * \brief Argument parser implementation
20  */
21 
22 #include "flang/ADT/hash.h"
23 #include "flang/ArgParser/arg_parser.h"
24 #include "flang/ArgParser/xflag.h"
25 #include "flang/Error/pgerror.h"
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 /** \brief Internal representation of argument parser */
31 struct arg_parser_ {
32   /** Hash map from string keys to instances of value data structure */
33   hashmap_t flags;
34   /** Registered values */
35   hashset_t values;
36   /** Values set by the parser */
37   hashset_t value_hits;
38   /** Set to true to throw an error on unknown arguments, to false to silently
39    * continue parsing */
40   bool fail_on_unknown_args;
41   /** Where to write input file name */
42   char **input_file_name_ptr;
43 };
44 
45 /** \brief Link a bool * and a char ** value together */
46 typedef struct bool_string_ {
47   bool *bool_ptr;
48   char **string_ptr;
49 } bool_string_t;
50 
51 /** \brief Combine input and output for action map arguments */
52 typedef struct action_map_bundle_ {
53   action_map_t *input;
54   action_map_t *output;
55 } action_map_bundle_t;
56 
57 /** \brief Argument value type */
58 typedef enum value_type_ {
59   ARG_ActionMap,
60   ARG_Boolean,
61   ARG_CombinedBoolean,
62   ARG_InformLevel,
63   ARG_Integer,
64   ARG_ReverseBoolean,
65   ARG_String,
66   ARG_StringList,
67   ARG_QFlag,
68   ARG_XFlag,
69   ARG_YFlag,
70 } value_type_t;
71 
72 /** \brief Argument type and location */
73 typedef struct value_ {
74   /** Type of value to write */
75   value_type_t type;
76   /** Where to write it */
77   void *location;
78 } value_t;
79 
80 static void add_generic_argument(arg_parser_t *parser, const char *arg_name,
81                                  value_type_t value_type, void *value_ptr);
82 static void deallocate_arg_value(hash_key_t ignore, hash_data_t value_ptr,
83                                  void *ignore_context);
84 static void compose_and_throw(const char *first_part, const char *second_part);
85 static char *next_value(char **argv, int *arg_index);
86 static inform_level_t strtoinform(const char *string);
87 
88 /** Allocate argument parser hash map */
89 void
create_arg_parser(arg_parser_t ** parser,bool fail_on_unknown_args)90 create_arg_parser(arg_parser_t **parser, bool fail_on_unknown_args)
91 {
92   *parser = (arg_parser_t*) malloc(sizeof(arg_parser_t));
93   (*parser)->flags = hashmap_alloc(hash_functions_strings);
94   (*parser)->values = hashset_alloc(hash_functions_direct);
95   (*parser)->value_hits = hashset_alloc(hash_functions_direct);
96   (*parser)->fail_on_unknown_args = fail_on_unknown_args;
97   (*parser)->input_file_name_ptr = NULL;
98 }
99 
100 /** Deallocate argument parser hash map */
101 void
destroy_arg_parser(arg_parser_t ** parser)102 destroy_arg_parser(arg_parser_t **parser)
103 {
104   /* Deallocate entries */
105   hashmap_iterate((*parser)->flags, deallocate_arg_value, NULL);
106 
107   /* Free flags data structure */
108   hashmap_free((*parser)->flags);
109 
110   /* Deallocate the data structure itself */
111   free(*parser);
112   *parser = NULL;
113 }
114 
115 /** Deallocate hashtable entry */
116 static void
deallocate_arg_value(hash_key_t key,hash_data_t value_ptr,void * key_context)117 deallocate_arg_value(hash_key_t key, hash_data_t value_ptr, void *key_context)
118 {
119   /* Some of the argument types require deallocation */
120   switch (((value_t *)value_ptr)->type) {
121   case ARG_ActionMap:
122   case ARG_CombinedBoolean:
123     free(((value_t *)value_ptr)->location);
124     break;
125   case ARG_ReverseBoolean:
126     free((char *)key);
127     break;
128   default:
129     /* Do nothing */
130     break;
131   }
132   free((value_t *)value_ptr);
133 }
134 
135 /** Register a string argument */
136 void
register_string_arg(arg_parser_t * parser,const char * arg_name,char ** target,const char * default_value)137 register_string_arg(arg_parser_t *parser, const char *arg_name, char **target,
138                     const char *default_value)
139 {
140   /* Set default value */
141   *target = (char *)default_value;
142 
143   add_generic_argument(parser, arg_name, ARG_String, (void *)target);
144 }
145 
146 /** Register a string list argument */
147 void
register_string_list_arg(arg_parser_t * parser,const char * arg_name,char ** target)148 register_string_list_arg(arg_parser_t *parser, const char *arg_name,
149                          char **target)
150 {
151   /* Terminate list */
152   *target = NULL;
153 
154   add_generic_argument(parser, arg_name, ARG_StringList, (void *)target);
155 }
156 
157 /** Register an integer argument */
158 void
register_integer_arg(arg_parser_t * parser,const char * arg_name,int * target,const int default_value)159 register_integer_arg(arg_parser_t *parser, const char *arg_name, int *target,
160                      const int default_value)
161 {
162   /* Set default value */
163   *target = (int)default_value;
164 
165   add_generic_argument(parser, arg_name, ARG_Integer, (void *)target);
166 }
167 
168 /** Register a boolean argument */
169 void
register_boolean_arg(arg_parser_t * parser,const char * arg_name,bool * target,const bool default_value)170 register_boolean_arg(arg_parser_t *parser, const char *arg_name, bool *target,
171                      const bool default_value)
172 {
173   /* Space to write reverse argument name, deallocation is done in
174    * deallocate_arg_value */
175   char *reverse_arg_name = (char*) malloc(strlen(arg_name) + 3);
176 
177   /* -no<arg> */
178   strcpy(reverse_arg_name, "no");
179   strcat(reverse_arg_name, arg_name);
180 
181   /* Set default value */
182   *target = (bool)default_value;
183 
184   /* -<arg> */
185   add_generic_argument(parser, arg_name, ARG_Boolean, (void *)target);
186   /* -no<arg> */
187   add_generic_argument(parser, reverse_arg_name, ARG_ReverseBoolean,
188                        (void *)target);
189 }
190 
191 /** Register a combines bool and string argument */
192 void
register_combined_bool_string_arg(arg_parser_t * parser,const char * arg_name,bool * bool_target,char ** string_target)193 register_combined_bool_string_arg(arg_parser_t *parser, const char *arg_name,
194                                   bool *bool_target, char **string_target)
195 {
196   /* Boolean target defaults to false (not set) */
197   *bool_target = false;
198   /* and string target defaults to NULL */
199   *string_target = NULL;
200 
201   /* Store both pointers for argument processsing */
202   bool_string_t *target = (bool_string_t*) malloc(sizeof(bool_string_t));
203   target->bool_ptr = bool_target;
204   target->string_ptr = string_target;
205 
206   add_generic_argument(parser, arg_name, ARG_CombinedBoolean, (void *)target);
207 }
208 
209 /** Register "x" flag argument */
210 void
register_qflag_arg(arg_parser_t * parser,const char * arg_name,int * qflags,const int qflags_size)211 register_qflag_arg(arg_parser_t *parser, const char *arg_name, int *qflags,
212                    const int qflags_size)
213 {
214   /* Fill array with zeros */
215   memset(qflags, 0, qflags_size * sizeof(int));
216 
217   add_generic_argument(parser, arg_name, ARG_QFlag, (void *)qflags);
218 }
219 
220 /** Register "x" flag argument */
221 void
register_xflag_arg(arg_parser_t * parser,const char * arg_name,int * xflags,const int xflags_size)222 register_xflag_arg(arg_parser_t *parser, const char *arg_name, int *xflags,
223                    const int xflags_size)
224 {
225   /* Fill xflags array with zeros */
226   memset(xflags, 0, xflags_size * sizeof(int));
227 
228   add_generic_argument(parser, arg_name, ARG_XFlag, (void *)xflags);
229 }
230 
231 /** Register "y" flag argument */
232 void
register_yflag_arg(arg_parser_t * parser,const char * arg_name,int * xflags)233 register_yflag_arg(arg_parser_t *parser, const char *arg_name, int *xflags)
234 {
235   add_generic_argument(parser, arg_name, ARG_YFlag, (void *)xflags);
236 }
237 
238 /** Register verbosity argument */
239 void
register_inform_level_arg(arg_parser_t * parser,const char * arg_name,inform_level_t * target,const inform_level_t default_value)240 register_inform_level_arg(arg_parser_t *parser, const char *arg_name,
241                           inform_level_t *target,
242                           const inform_level_t default_value)
243 {
244   /* Set default value */
245   *target = (inform_level_t)default_value;
246 
247   add_generic_argument(parser, arg_name, ARG_InformLevel, (void *)target);
248 }
249 
250 /** Register "action list" argument */
251 void
register_action_map_arg(arg_parser_t * parser,const char * arg_name,action_map_t * target,const action_map_t * input)252 register_action_map_arg(arg_parser_t *parser, const char *arg_name,
253                         action_map_t *target, const action_map_t *input)
254 {
255   action_map_bundle_t *value = (action_map_bundle_t*) malloc(
256       sizeof(action_map_bundle_t));
257   value->input = (action_map_t *)input;
258   value->output = target;
259 
260   add_generic_argument(parser, arg_name, ARG_ActionMap, (void *)value);
261 }
262 
263 /** Register input file name */
264 void
register_filename_arg(arg_parser_t * parser,char ** target)265 register_filename_arg(arg_parser_t *parser, char **target)
266 {
267   parser->input_file_name_ptr = target;
268 }
269 
270 /** \brief Add a generic argument, specifying argument type
271  *
272  * \param arg_parser  Parser data structure
273  * \param arg_name    Argument name (key to flags table)
274  * \param value_type  Type of the value to store
275  * \param value_ptr   Location to store value to
276  */
277 static void
add_generic_argument(arg_parser_t * parser,const char * arg_name,value_type_t value_type,void * value_ptr)278 add_generic_argument(arg_parser_t *parser, const char *arg_name,
279                      value_type_t value_type, void *value_ptr)
280 {
281   /* Mapped value */
282   value_t *value = NULL;
283   /* Old (ignored) value */
284   hash_data_t old_value = NULL;
285 
286   /* Check if this argument is already registered */
287   if (hashmap_lookup(parser->flags, arg_name, &old_value)) {
288     /* Compose & throw error */
289     compose_and_throw("Argument already registered: ", arg_name);
290   }
291 
292   /* Add value to flags hashmap */
293   value = (value_t*) malloc(sizeof(value_t));
294   value->type = value_type;
295   value->location = value_ptr;
296   hashmap_insert(parser->flags, arg_name, value);
297 
298   /* Put the same value into hashset tracking registered values (this time it is
299    * the key) */
300   if (value_type == ARG_ActionMap) {
301     action_map_t *target = ((action_map_bundle_t *)value_ptr)->output;
302     hashset_replace(parser->values, target);
303   } else {
304     hashset_replace(parser->values, value_ptr);
305   }
306 }
307 
308 /** Parse all arguments */
309 void
parse_arguments(const arg_parser_t * parser,int argc,char ** argv)310 parse_arguments(const arg_parser_t *parser, int argc, char **argv)
311 {
312   int argindex = 1;     /* Argument counter */
313   char *next_string;    /* Next argument */
314   int x_index, x_value; /* Index to xflags array and value to set there */
315 
316   /* Reset value hits */
317   hashset_clear(parser->value_hits);
318 
319   /* Make sure we can set file name */
320   if (!parser->input_file_name_ptr) {
321     interr("Input file name is not registered", 0, ERR_Fatal);
322   }
323 
324   /* First grab the source file name */
325   if (*argv[1] != '-') {
326     *parser->input_file_name_ptr = argv[1];
327     argindex = 2;
328   } else if (*argv[argc - 1] != '-') {
329     *parser->input_file_name_ptr = argv[argc - 1];
330     --argc;
331   }
332 
333   /* Loop through provided arguments */
334   while (argindex < argc) {
335     value_t *value = NULL;
336     char *arg = argv[argindex];
337 
338     /* All switches should start with a '-' */
339     if (*arg != '-') {
340       compose_and_throw("Unrecognized argument: ", arg);
341     }
342     ++arg; /* skip over '-' */
343 
344     /* All arguments need to be in the data structure */
345     if (!hashmap_lookup(parser->flags, arg, (hash_data_t *)&value)) {
346       if (parser->fail_on_unknown_args) {
347         compose_and_throw("Unknown command line argument: ", arg);
348       } else {
349         /* Skip until next switch or end of argument list */
350         while ((argindex < argc - 1) && (*argv[++argindex] != '-'))
351           ;
352         if (argindex == argc - 1)
353           break;
354         continue;
355       }
356     }
357 
358     /* Parse argument type */
359     switch (value->type) {
360     case ARG_ActionMap: {
361       action_map_t *from = ((action_map_bundle_t *)value->location)->input;
362       action_map_t *to = ((action_map_bundle_t *)value->location)->output;
363 
364       /* TODO parse lists of arguments */
365       char *phase_string = next_value(argv, &argindex);
366       char *dump_string = next_value(argv, &argindex);
367 
368       copy_action(from, dump_string, to, phase_string);
369     } break;
370 
371     case ARG_Boolean:
372       *((bool *)value->location) = true;
373       break;
374 
375     case ARG_ReverseBoolean:
376       *((bool *)value->location) = false;
377       break;
378 
379     case ARG_String:
380       next_string = next_value(argv, &argindex);
381       if (!next_string)
382         compose_and_throw("Missing value for -", arg);
383       /* Change stored value to point to passed string */
384       *((char **)value->location) = next_string;
385       break;
386 
387     case ARG_StringList:
388       /* Get argument value */
389       next_string = next_value(argv, &argindex);
390       if (!next_string)
391         compose_and_throw("Missing value for -", arg);
392       /* Set argument value */
393       *((char **)value->location) = next_string;
394       /* Point to the next value on the list */
395       value->location = (void *)(((char **)value->location) + 1);
396       /* Terminate the list */
397       *((char **)value->location) = NULL;
398       break;
399 
400     case ARG_InformLevel:
401       next_string = next_value(argv, &argindex);
402       if (!next_string)
403         compose_and_throw("Missing value for -", arg);
404       *((inform_level_t *)value->location) = strtoinform(next_string);
405       break;
406 
407     case ARG_Integer:
408       next_string = next_value(argv, &argindex);
409       if (!next_string)
410         compose_and_throw("Missing value for -", arg);
411       *((int *)value->location) = (int)strtol(next_string, NULL, 10);
412       break;
413 
414     case ARG_QFlag:
415       next_string = next_value(argv, &argindex);
416 
417       if (!next_string)
418         compose_and_throw("Missing value for -", arg);
419 
420       x_index = (int)strtol(next_string, NULL, 10);
421 
422       next_string = next_value(argv, &argindex);
423 
424       if (!next_string)
425         compose_and_throw("Missing second value for -", arg);
426 
427       x_value = (int)strtol(next_string, NULL, 10);
428 
429       ((int *)value->location)[x_index] |= x_value;
430       break;
431 
432     case ARG_XFlag:
433       next_string = next_value(argv, &argindex);
434 
435       if (!next_string)
436         compose_and_throw("Missing value for -", arg);
437       x_index = (int)strtol(next_string, NULL, 10);
438 
439       next_string = next_value(argv, &argindex);
440 
441       if (next_string)
442         x_value = (int)strtol(next_string, NULL, 0);
443       else
444         x_value = 1;
445 
446       set_xflag_value((int *)value->location, x_index, x_value);
447       break;
448 
449     case ARG_YFlag:
450       next_string = next_value(argv, &argindex);
451 
452       if (!next_string)
453         compose_and_throw("Missing value for -", arg);
454       x_index = (int)strtol(next_string, NULL, 10);
455 
456       next_string = next_value(argv, &argindex);
457 
458       if (next_string)
459         x_value = (int)strtol(next_string, NULL, 0);
460       else
461         x_value = 1;
462 
463       unset_xflag_value((int *)value->location, x_index, x_value);
464       break;
465 
466     case ARG_CombinedBoolean:
467       *(((bool_string_t *)value->location)->bool_ptr) = true;
468       next_string = next_value(argv, &argindex);
469       if (next_string) {
470         *(((bool_string_t *)value->location)->string_ptr) = next_string;
471       }
472       break;
473 
474     default:
475       interr("Uknown command line argument value type", value->type, ERR_Fatal);
476       break;
477     }
478 
479     /* Remember that the value as set */
480     if (value->type == ARG_ActionMap) {
481       action_map_t *target = ((action_map_bundle_t *)value->location)->output;
482       hashset_replace(parser->value_hits, target);
483     } else {
484       hashset_replace(parser->value_hits, value->location);
485     }
486 
487     ++argindex;
488   }
489 }
490 
491 /** \brief Compose and throw internal compiler error
492  *
493  * Produces internal compiler error with the message composed by concatenating
494  * the two operands
495  */
496 static void
compose_and_throw(const char * first_part,const char * second_part)497 compose_and_throw(const char *first_part, const char *second_part)
498 {
499   char *msg = (char*) malloc(strlen(first_part) + strlen(second_part) + 1);
500   strcpy(msg, first_part);
501   strcat(msg, second_part);
502   interr(msg, 0, ERR_Fatal);
503 }
504 
505 /** \brief Point to next argument value
506  *
507  * If next argument in argument list does not start with a '-' return it and
508  * advance argument counter, otherwise return NULL and keep counter the same
509  *
510  * \return          pointer to the next non-switch command line argument
511  * \param argv      command line argument array
512  * \param arg_index current index into the argument array
513  */
514 static char *
next_value(char ** argv,int * arg_index)515 next_value(char **argv, int *arg_index)
516 {
517   if (*argv[*arg_index + 1] == '-')
518     return NULL;
519 
520   *arg_index = *arg_index + 1;
521 
522   return argv[*arg_index];
523 }
524 
525 /** \brief Convert a inform level string to corresponding constant */
526 static inform_level_t
strtoinform(const char * string)527 strtoinform(const char *string)
528 {
529   if (!strcmp("inform", string))
530     return LV_Inform;
531   if (!strcmp("severe", string))
532     return LV_Severe;
533   if (!strcmp("warn", string))
534     return LV_Warn;
535   if (!strcmp("fatal", string))
536     return LV_Fatal;
537 
538   compose_and_throw("Unrecognized inform level: ", string);
539 
540   /* unreacheable */
541   return LV_Inform;
542 }
543 
544 /** Check if argument was found during parse */
545 bool
was_value_set(const arg_parser_t * parser,const void * location)546 was_value_set(const arg_parser_t *parser, const void *location)
547 {
548   /* Any value passed to this function need to be registered */
549   if (!hashset_lookup(parser->values, location)) {
550     compose_and_throw(__func__, ": value location not registered");
551   }
552 
553   return (hashset_lookup(parser->value_hits, location));
554 }
555