FOSSology  4.4.0
Open Source License Compliance by Open Source Software
fossconfig.c
Go to the documentation of this file.
1 /*
2  SPDX-FileCopyrightText: © 2011-2013 Hewlett-Packard Development Company, L.P.
3 
4  SPDX-License-Identifier: GPL-2.0-only
5 */
13 /* local includes */
14 #include <fossconfig.h>
15 
16 /* std library includes */
17 #include <ctype.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 
22 /* glib includes */
23 #include <glib.h>
24 
25 /* ************************************************************************** */
26 /* *** utility ************************************************************** */
27 /* ************************************************************************** */
28 
44 static const gchar* fo_conf_pattern = "\
45  (?<comment>;.*)\n|\
46  (?<group> \\[ (?:[ \t]*) (?<gname>[\\w\\d_]+) (?:[ \t]*) \\]) \n|\
47  (?<key> ([\\.\\w\\d_-]+)) (?:[ \t]*) = (?:[ \t]*)(?<value>.*)\n|\
48  (?<klist> ([\\.\\w\\d_-]+))\\[\\](?:[ \t]*) = (?:[ \t]*)(?<vlist>.*)\n|\
49  (?<error> (?:\\S+)(?:[ \t]*))\n";
50 
55 static const gchar* fo_conf_variable = "\\$(\\w+)";
56 
57 GRegex* fo_conf_parse;
58 GRegex* fo_conf_replace;
59 
69 static gint str_comp(gconstpointer a, gconstpointer b, gpointer user_data)
70 {
71  return strcmp((char*) a, (char*) b);
72 }
73 
85 static gboolean collect_keys(char* key, gpointer* value, char** data)
86 {
87  int idx = 0;
88 
89  /* find first empty key */
90  while (data[idx])
91  idx++;
92 
93  data[idx] = key;
94  return FALSE;
95 }
96 
97 /* ************************************************************************** */
98 /* *** private functions **************************************************** */
99 /* ************************************************************************** */
100 
101 #define BUFFER_SIZE 4096
102 #define yynext() (c = next()) != EOF
104 #define throw_error(error, domain, code, ...) \
105  { g_set_error(error, domain, code, __VA_ARGS__); \
106  return 0; }
107 
117 static gboolean fo_config_sub(const GMatchInfo* match, GString* ret,
118  gpointer data)
119 {
120  GTree* group = (GTree*) data;
121  gchar* key = g_match_info_fetch(match, 1);
122  gchar* sub = g_tree_lookup(group, key);
123 
124  g_string_append(ret, sub);
125  g_free(key);
126 
127  return FALSE;
128 }
129 
147 static int fo_config_key(GTree* group, gchar* key, gchar* val, gboolean list,
148  gchar* fname, guint line, GError** error)
149 {
150  gchar* tmp = g_regex_replace_eval(fo_conf_replace, val, -1, 0, 0,
151  fo_config_sub, group, NULL);
152 
153  if (group == NULL)
154  throw_error(
155  error,
156  PARSE_ERROR,
158  "%s[line %d]: key \"%s\" does not have an associated group",
159  fname, line, key);
160 
161  if (list)
162  {
163  if ((val = g_tree_lookup(group, key)))
164  {
165  val = g_strdup_printf("%s[%s]", val, tmp);
166  g_free(tmp);
167  }
168  else
169  {
170  val = g_strdup_printf("[%s]", tmp);
171  g_free(tmp);
172  }
173  }
174  else
175  {
176  val = tmp;
177  }
178 
179  g_tree_insert(group, g_strdup(key), val);
180  return 1;
181 }
182 
202 static gboolean fo_config_eval(const GMatchInfo* match, GTree** g_current,
203  fo_conf* dest, gchar* yyfile, guint yyline, GError** error)
204 {
205  gchar* error_t = NULL;
206 
207  /* check to make sure we haven't hit an error */
208  if ((error_t = g_match_info_fetch_named(match, "error")) != NULL)
209  {
210  if (strlen(error_t) > 0)
211  {
212  g_set_error(error, PARSE_ERROR, fo_invalid_file,
213  "%s[line %d]: incorrectly formated line \"%s\".",
214  yyfile, yyline, error_t);
215  g_free(error_t);
216  return TRUE;
217  }
218  else
219  {
220  g_free(error_t);
221  }
222  }
223 
224  gchar* group = g_match_info_fetch_named(match, "group");
225  gchar* gname = g_match_info_fetch_named(match, "gname");
226  gchar* key = g_match_info_fetch_named(match, "key");
227  gchar* value = g_match_info_fetch_named(match, "value");
228  gchar* klist = g_match_info_fetch_named(match, "klist");
229  gchar* vlist = g_match_info_fetch_named(match, "vlist");
230  gchar* wrong = g_match_info_fetch_named(match, "error");
231 
232  if (group != NULL && group[0])
233  {
234  *g_current = g_tree_new_full(str_comp, NULL, g_free, g_free);
235  g_tree_insert(dest->group_map, g_strdup(gname), *g_current);
236  }
237  else if (key != NULL && key[0])
238  {
239  if (!fo_config_key(*g_current, key, value, FALSE, yyfile, yyline, error))
240  return TRUE;
241  }
242  else if (klist != NULL && klist[0])
243  {
244  if (!fo_config_key(*g_current, klist, vlist, TRUE, yyfile, yyline, error))
245  return TRUE;
246  }
247 
248  g_free(group);
249  g_free(gname);
250  g_free(key);
251  g_free(value);
252  g_free(klist);
253  g_free(vlist);
254  g_free(wrong);
255 
256  return FALSE;
257 }
258 
259 /* ************************************************************************** */
260 /* *** public interface ***************************************************** */
261 /* ************************************************************************** */
262 
275 fo_conf* fo_config_load(char* rawname, GError** error)
276 {
277  fo_conf* ret;
278  gchar text[BUFFER_SIZE];
279  guint yyline = 1;
280  FILE* fd;
281  GMatchInfo* match;
282 
283  if (rawname == NULL)
284  return NULL;
285 
286  if (fo_conf_parse == NULL)
287  fo_conf_parse = g_regex_new(fo_conf_pattern,
288  G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, 0, NULL);
289  if (fo_conf_replace == NULL)
290  fo_conf_replace = g_regex_new(fo_conf_variable,
291  G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, 0, NULL);
292 
293  if ((fd = fopen(rawname, "r")) == NULL)
295  "unable to open configuration file \"%s\"", rawname);
296 
297  ret = g_new0(fo_conf, 1);
298  ret->group_map = NULL;
299  ret->key_sets = NULL;
300  ret->group_set = NULL;
301  ret->n_groups = 0;
302  ret->group_map = g_tree_new_full(str_comp, NULL, g_free,
303  (GDestroyNotify) g_tree_unref);
304 
305  GTree* g_current = NULL;
306 
307  while (fgets(text, sizeof(text), fd) != NULL)
308  {
309  if (g_regex_match(fo_conf_parse, text, 0, &match))
310  {
311  fo_config_eval(match, &g_current, ret, rawname, yyline, error);
312 
313  if (*error)
314  return NULL;
315  }
316 
317  g_match_info_free(match);
318  match = NULL;
319  yyline++;
320  }
321 
322  fclose(fd);
323 
324  return ret;
325 }
326 
336 char* fo_config_get(fo_conf* conf, const char* group, const char* key,
337  GError** error)
338 {
339  GTree* tree;
340  char* ret = NULL;
341 
342  if (!conf || conf->group_map == NULL)
343  throw_error(
344  error,
345  RETRIEVE_ERROR,
347  "ERROR: invalid fo_conf object passed to fo_config_get");
348 
349  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
350  throw_error(
351  error,
352  RETRIEVE_ERROR,
354  "ERROR: unknown group \"%s\"", group);
355 
356  if ((ret = g_tree_lookup(tree, key)) == NULL)
357  throw_error(
358  error,
359  RETRIEVE_ERROR,
361  "ERROR: unknown key=\"%s\" for group=\"%s\"", key, group);
362 
363  return g_tree_lookup(tree, key);
364 }
365 
382 char* fo_config_get_list(fo_conf* conf, char* group, char* key, int idx,
383  GError** error)
384 {
385  char* val;
386  int depth;
387  char* curr;
388 
389 
390  if (!conf || conf->group_map == NULL)
391  throw_error(
392  error,
393  RETRIEVE_ERROR,
395  "ERROR: invalid fo_conf object passed to fo_config_get_list");
396 
397  if (!fo_config_is_list(conf, group, key, error)) if (!(*error))
398  throw_error(
399  error,
400  RETRIEVE_ERROR,
402  "ERROR: %s[%s] must be of type list to get list element", group, key);
403 
404  if (idx < 0 || idx >= fo_config_list_length(conf, group, key, error))
405  throw_error(
406  error,
407  RETRIEVE_ERROR,
409  "ERROR: %s[%s] %d is out of range", group, key, idx);
410 
411  if (error && *error)
412  return NULL;
413 
414  val = g_tree_lookup(
415  g_tree_lookup(conf->group_map, group), key);
416 
417  curr = val;
418  for (depth = 0; depth < idx;)
419  {
420  while (*(++curr) != '[');
421  depth++;
422  }
423 
424  val = curr + 1;
425  while (*(++curr) != ']');
426  val = g_strndup(val, curr - val);
427 
428  return val;
429 }
430 
439 int fo_config_is_list(fo_conf* conf, char* group, char* key, GError** error)
440 {
441  GTree* tree;
442  char* val;
443 
444  if (!conf || conf->group_map == NULL)
445  throw_error(
446  error,
447  RETRIEVE_ERROR,
449  "ERROR: invalid fo_conf object passed to fo_config_is_list");
450 
451  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
452  throw_error(
453  error,
454  RETRIEVE_ERROR,
456  "ERROR: unknown group \"%s\"", group);
457 
458  if ((val = g_tree_lookup(tree, key)) == NULL)
459  throw_error(
460  error,
461  RETRIEVE_ERROR,
463  "ERROR: unknown key/value expression \"%s\"", key);
464 
465  return val[0] == '[';
466 }
467 
475 int fo_config_list_length(fo_conf* conf, char* group, char* key, GError** error)
476 {
477  char* val;
478  char* curr;
479  int count = 0;
480 
481  if (!fo_config_is_list(conf, group, key, error))
482  throw_error(
483  error,
484  RETRIEVE_ERROR,
486  "ERROR: %s[%s] must be of type list to get length", group, key);
487  if (error && *error)
488  return 0;
489 
490  val = g_tree_lookup(
491  g_tree_lookup(conf->group_map, group), key);
492 
493  for (curr = val; *curr; curr++)
494  if (*curr == '[')
495  count++;
496 
497  return count;
498 }
499 
507 {
508  if (!conf) return;
509  if (conf->group_map) g_tree_unref(conf->group_map);
510  if (conf->key_sets) g_tree_unref(conf->key_sets);
511  if (conf->group_set) g_free(conf->group_set);
512 
513  conf->group_map = NULL;
514  conf->key_sets = NULL;
515  conf->group_set = NULL;
516 
517  g_free(conf);
518 }
519 
531 void fo_config_join(fo_conf* dst, fo_conf* src, GError** error)
532 {
533  int ngroups, i;
534  char** groups = fo_config_group_set(src, &ngroups);
535  GTree* keys;
536 
537  /* before making any changes, check that there are no conflicts */
538  for (i = 0; i < ngroups; i++)
539  {
540  if (fo_config_has_group(dst, groups[i]))
541  {
542  g_set_error(error, RETRIEVE_ERROR, fo_invalid_join,
543  "Cannot join configuration with conflicting group \"%s\"", groups[i]);
544  return;
545  }
546  }
547 
548  /* join the two configurations */
549  for (i = 0; i < ngroups; i++)
550  {
551  keys = g_tree_lookup(src->group_map, groups[i]);
552  keys = g_tree_ref(keys);
553  g_tree_insert(dst->group_map, g_strdup(groups[i]), keys);
554  }
555 }
556 
557 /* ************************************************************************** */
558 /* *** special interface **************************************************** */
559 /* ************************************************************************** */
560 
572 char** fo_config_group_set(fo_conf* conf, int* length)
573 {
574  if (!conf)
575  {
576  *length = 0;
577  return NULL;
578  }
579 
580  if (conf->group_set)
581  {
582  *length = conf->n_groups;
583  return conf->group_set;
584  }
585 
586  if (conf->group_map == NULL)
587  {
588  *length = 0;
589  return NULL;
590  }
591 
592  *length = g_tree_nnodes(conf->group_map);
593  conf->n_groups = *length;
594  conf->group_set = g_new0(
595  char*, *length);
596  g_tree_foreach(conf->group_map, (GTraverseFunc) collect_keys, conf->group_set);
597 
598  return conf->group_set;
599 }
600 
614 char** fo_config_key_set(fo_conf* conf, char* group, int* length)
615 {
616  GTree* tree;
617  char** ret;
618  *length = 0;
619 
620  if (!conf)
621  return NULL;
622 
623  if (!conf->key_sets)
624  conf->key_sets = g_tree_new_full(str_comp, NULL, g_free, g_free);
625 
626  if (conf->group_map == NULL)
627  return NULL;
628 
629  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
630  return NULL;
631  *length = g_tree_nnodes(tree);
632 
633  if ((ret = g_tree_lookup(conf->key_sets, group)))
634  return ret;
635 
636  ret = g_new0(
637  char*, *length);
638  g_tree_foreach(tree, (GTraverseFunc) collect_keys, ret);
639  g_tree_insert(conf->key_sets, g_strdup(group), ret);
640 
641  return ret;
642 }
643 
651 int fo_config_has_group(fo_conf* conf, char* group)
652 {
653  if (conf == NULL)
654  return 0;
655  if (!conf->group_map)
656  return 0;
657  return g_tree_lookup(conf->group_map, group) != NULL;
658 }
659 
668 int fo_config_has_key(fo_conf* conf, char* group, char* key)
669 {
670  GTree* tree;
671 
672  if (conf == NULL)
673  return 0;
674  if (!conf->group_map)
675  return 0;
676  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
677  return 0;
678  return g_tree_lookup(tree, key) != NULL;
679 }
680 
690 char* trim(char* ptext)
691 {
692  if (ptext && ptext[0])
693  {
694  int len = strlen(ptext);
695  while (isspace(ptext[len - 1])) ptext[--len] = 0; // right trim
696  while (isspace(*ptext)) ++ptext; // left trim
697  }
698 
699  return ptext;
700 }
701 
fo_conf * conf
The loaded configuration data.
Definition: fo_cli.c:39
char * fo_config_get_list(fo_conf *conf, char *group, char *key, int idx, GError **error)
Definition: fossconfig.c:382
int fo_config_list_length(fo_conf *conf, char *group, char *key, GError **error)
Gets the length of the list associated with a particular list key.
Definition: fossconfig.c:475
static gboolean fo_config_sub(const GMatchInfo *match, GString *ret, gpointer data)
Definition: fossconfig.c:117
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690
void fo_config_free(fo_conf *conf)
Frees the memory associated with the internal configuration data structures.
Definition: fossconfig.c:506
char ** fo_config_group_set(fo_conf *conf, int *length)
Gets the set of group names.
Definition: fossconfig.c:572
#define BUFFER_SIZE
Maximum buffer length.
Definition: fossconfig.c:101
static const gchar * fo_conf_variable
Definition: fossconfig.c:55
GRegex * fo_conf_parse
Regex for parsing.
Definition: fossconfig.c:57
static int fo_config_key(GTree *group, gchar *key, gchar *val, gboolean list, gchar *fname, guint line, GError **error)
Inserts a new Key/Value pair into the mapping of keys to values.
Definition: fossconfig.c:147
fo_conf * fo_config_load(char *rawname, GError **error)
Load the configuration information from the provided file.
Definition: fossconfig.c:275
static gint str_comp(gconstpointer a, gconstpointer b, gpointer user_data)
Definition: fossconfig.c:69
static gboolean fo_config_eval(const GMatchInfo *match, GTree **g_current, fo_conf *dest, gchar *yyfile, guint yyline, GError **error)
Decides what to do with any one line of an input file.
Definition: fossconfig.c:202
static const gchar * fo_conf_pattern
Definition: fossconfig.c:44
GRegex * fo_conf_replace
Regex for replace.
Definition: fossconfig.c:58
char * fo_config_get(fo_conf *conf, const char *group, const char *key, GError **error)
Gets an element based on its group name and key name. If the group or key is not found,...
Definition: fossconfig.c:336
#define throw_error(error, domain, code,...)
Definition: fossconfig.c:104
int fo_config_has_group(fo_conf *conf, char *group)
Checks if the currently parsed configuration file has a specific group.
Definition: fossconfig.c:651
int fo_config_is_list(fo_conf *conf, char *group, char *key, GError **error)
Checks if a particular value is a list or just a normal value.
Definition: fossconfig.c:439
static gboolean collect_keys(char *key, gpointer *value, char **data)
Definition: fossconfig.c:85
void fo_config_join(fo_conf *dst, fo_conf *src, GError **error)
Takes all groups and key from a fo_conf and adds them to another.
Definition: fossconfig.c:531
char ** fo_config_key_set(fo_conf *conf, char *group, int *length)
Gets the set of key names for a particular group.
Definition: fossconfig.c:614
int fo_config_has_key(fo_conf *conf, char *group, char *key)
Checks if the a specific group in the currently parsed configuration file has a specific key.
Definition: fossconfig.c:668
FOSSology library to read config file.
#define PARSE_ERROR
Definition: fossconfig.h:16
@ fo_load_config
Unable to load config.
Definition: fossconfig.h:29
@ fo_missing_key
Required key is missing.
Definition: fossconfig.h:24
@ fo_invalid_join
Join is invalid.
Definition: fossconfig.h:28
@ fo_invalid_file
File is invalid.
Definition: fossconfig.h:27
@ fo_missing_file
File is missing.
Definition: fossconfig.h:22
@ fo_invalid_group
Requested group is invalid.
Definition: fossconfig.h:26
@ fo_missing_group
Required group is missing.
Definition: fossconfig.h:23
@ fo_invalid_key
Requested key is invalid.
Definition: fossconfig.h:25
char ** group_set
Array of groups.
Definition: fossconfig.h:37
int n_groups
Number of groups.
Definition: fossconfig.h:38
GTree * group_map
Tree of groups in conf file.
Definition: fossconfig.h:35
GTree * key_sets
Tree of sets of keys.
Definition: fossconfig.h:36
list_t type structure used to keep various lists. (e.g. there are multiple lists).
Definition: nomos.h:308
Store the results of a regex match.
Definition: scanners.hpp:28