FOSSology  3.2.0rc1
Open Source License Compliance by Open Source Software
fossconfig.c
Go to the documentation of this file.
1 /* **************************************************************
2  Copyright (C) 2011-2013 Hewlett-Packard Development Company, L.P.
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public License
6  version 2 as published by the Free Software Foundation.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License along
14  with this program; if not, write to the Free Software Foundation, Inc.,
15  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 
17  ************************************************************** */
25 /* local includes */
26 #include <fossconfig.h>
27 
28 /* std library includes */
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 
34 /* glib includes */
35 #include <glib.h>
36 
37 /* ************************************************************************** */
38 /* *** utility ************************************************************** */
39 /* ************************************************************************** */
40 
56 static const gchar* fo_conf_pattern = "\
57  (?<comment>;.*)\n|\
58  (?<group> \\[ (?:[ \t]*) (?<gname>[\\w\\d_]+) (?:[ \t]*) \\]) \n|\
59  (?<key> ([\\.\\w\\d_-]+)) (?:[ \t]*) = (?:[ \t]*)(?<value>.*)\n|\
60  (?<klist> ([\\.\\w\\d_-]+))\\[\\](?:[ \t]*) = (?:[ \t]*)(?<vlist>.*)\n|\
61  (?<error> (?:\\S+)(?:[ \t]*))\n";
62 
67 static const gchar* fo_conf_variable = "\\$(\\w+)";
68 
69 GRegex* fo_conf_parse;
70 GRegex* fo_conf_replace;
71 
81 static gint str_comp(gconstpointer a, gconstpointer b, gpointer user_data)
82 {
83  return strcmp((char*) a, (char*) b);
84 }
85 
97 static gboolean collect_keys(char* key, gpointer* value, char** data)
98 {
99  int idx = 0;
100 
101  /* find first empty key */
102  while (data[idx])
103  idx++;
104 
105  data[idx] = key;
106  return FALSE;
107 }
108 
109 /* ************************************************************************** */
110 /* *** private functions **************************************************** */
111 /* ************************************************************************** */
112 
113 #define BUFFER_SIZE 4096
114 #define yynext() (c = next()) != EOF
115 
116 #define throw_error(error, domain, code, ...) \
117  { g_set_error(error, domain, code, __VA_ARGS__); \
118  return 0; }
119 
129 static gboolean fo_config_sub(const GMatchInfo* match, GString* ret,
130  gpointer data)
131 {
132  GTree* group = (GTree*) data;
133  gchar* key = g_match_info_fetch(match, 1);
134  gchar* sub = g_tree_lookup(group, key);
135 
136  g_string_append(ret, sub);
137  g_free(key);
138 
139  return FALSE;
140 }
141 
159 static int fo_config_key(GTree* group, gchar* key, gchar* val, gboolean list,
160  gchar* fname, guint line, GError** error)
161 {
162  gchar* tmp = g_regex_replace_eval(fo_conf_replace, val, -1, 0, 0,
163  fo_config_sub, group, NULL);
164 
165  if (group == NULL)
166  throw_error(
167  error,
168  PARSE_ERROR,
170  "%s[line %d]: key \"%s\" does not have an associated group",
171  fname, line, key);
172 
173  if (list)
174  {
175  if ((val = g_tree_lookup(group, key)))
176  {
177  val = g_strdup_printf("%s[%s]", val, tmp);
178  g_free(tmp);
179  }
180  else
181  {
182  val = g_strdup_printf("[%s]", tmp);
183  g_free(tmp);
184  }
185  }
186  else
187  {
188  val = tmp;
189  }
190 
191  g_tree_insert(group, g_strdup(key), val);
192  return 1;
193 }
194 
214 static gboolean fo_config_eval(const GMatchInfo* match, GTree** g_current,
215  fo_conf* dest, gchar* yyfile, guint yyline, GError** error)
216 {
217  gchar* error_t = NULL;
218 
219  /* check to make sure we haven't hit an error */
220  if ((error_t = g_match_info_fetch_named(match, "error")) != NULL)
221  {
222  g_set_error(error, PARSE_ERROR, fo_invalid_file,
223  "%s[line %d]: incorrectly formated line \"%s\".",
224  yyfile, yyline, error_t);
225  g_free(error_t);
226  return TRUE;
227  }
228 
229  gchar* group = g_match_info_fetch_named(match, "group");
230  gchar* gname = g_match_info_fetch_named(match, "gname");
231  gchar* key = g_match_info_fetch_named(match, "key");
232  gchar* value = g_match_info_fetch_named(match, "value");
233  gchar* klist = g_match_info_fetch_named(match, "klist");
234  gchar* vlist = g_match_info_fetch_named(match, "vlist");
235  gchar* wrong = g_match_info_fetch_named(match, "error");
236 
237  if (group != NULL && group[0])
238  {
239  *g_current = g_tree_new_full(str_comp, NULL, g_free, g_free);
240  g_tree_insert(dest->group_map, g_strdup(gname), *g_current);
241  }
242  else if (key != NULL && key[0])
243  {
244  if (!fo_config_key(*g_current, key, value, FALSE, yyfile, yyline, error))
245  return TRUE;
246  }
247  else if (klist != NULL && klist[0])
248  {
249  if (!fo_config_key(*g_current, klist, vlist, TRUE, yyfile, yyline, error))
250  return TRUE;
251  }
252 
253  g_free(group);
254  g_free(gname);
255  g_free(key);
256  g_free(value);
257  g_free(klist);
258  g_free(vlist);
259  g_free(wrong);
260 
261  return FALSE;
262 }
263 
264 /* ************************************************************************** */
265 /* *** public interface ***************************************************** */
266 /* ************************************************************************** */
267 
280 fo_conf* fo_config_load(char* rawname, GError** error)
281 {
282  fo_conf* ret;
283  gchar text[BUFFER_SIZE];
284  guint yyline = 1;
285  FILE* fd;
286  GMatchInfo* match;
287 
288  if (rawname == NULL)
289  return NULL;
290 
291  if (fo_conf_parse == NULL)
292  fo_conf_parse = g_regex_new(fo_conf_pattern,
293  G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, 0, NULL);
294  if (fo_conf_replace == NULL)
295  fo_conf_replace = g_regex_new(fo_conf_variable,
296  G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, 0, NULL);
297 
298  if ((fd = fopen(rawname, "r")) == NULL)
300  "unable to open configuration file \"%s\"", rawname);
301 
302  ret = g_new0(fo_conf, 1);
303  ret->group_map = NULL;
304  ret->key_sets = NULL;
305  ret->group_set = NULL;
306  ret->n_groups = 0;
307  ret->group_map = g_tree_new_full(str_comp, NULL, g_free,
308  (GDestroyNotify) g_tree_unref);
309 
310  GTree* g_current = NULL;
311 
312  while (fgets(text, sizeof(text), fd) != NULL)
313  {
314  if (g_regex_match(fo_conf_parse, text, 0, &match))
315  {
316  fo_config_eval(match, &g_current, ret, rawname, yyline, error);
317 
318  if (*error)
319  return NULL;
320  }
321 
322  g_match_info_free(match);
323  match = NULL;
324  yyline++;
325  }
326 
327  fclose(fd);
328 
329  return ret;
330 }
331 
341 char* fo_config_get(fo_conf* conf, const char* group, const char* key,
342  GError** error)
343 {
344  GTree* tree;
345  char* ret = NULL;
346 
347  if (!conf || conf->group_map == NULL)
348  throw_error(
349  error,
350  RETRIEVE_ERROR,
352  "ERROR: invalid fo_conf object passed to fo_config_get");
353 
354  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
355  throw_error(
356  error,
357  RETRIEVE_ERROR,
359  "ERROR: unknown group \"%s\"", group);
360 
361  if ((ret = g_tree_lookup(tree, key)) == NULL)
362  throw_error(
363  error,
364  RETRIEVE_ERROR,
366  "ERROR: unknown key=\"%s\" for group=\"%s\"", key, group);
367 
368  return g_tree_lookup(tree, key);
369 }
370 
387 char* fo_config_get_list(fo_conf* conf, char* group, char* key, int idx,
388  GError** error)
389 {
390  char* val;
391  int depth;
392  char* curr;
393 
394 
395  if (!conf || conf->group_map == NULL)
396  throw_error(
397  error,
398  RETRIEVE_ERROR,
400  "ERROR: invalid fo_conf object passed to fo_config_get_list");
401 
402  if (!fo_config_is_list(conf, group, key, error)) if (!(*error))
403  throw_error(
404  error,
405  RETRIEVE_ERROR,
407  "ERROR: %s[%s] must be of type list to get list element", group, key);
408 
409  if (idx < 0 || idx >= fo_config_list_length(conf, group, key, error))
410  throw_error(
411  error,
412  RETRIEVE_ERROR,
414  "ERROR: %s[%s] %d is out of range", group, key, idx);
415 
416  if (error && *error)
417  return NULL;
418 
419  val = g_tree_lookup(
420  g_tree_lookup(conf->group_map, group), key);
421 
422  curr = val;
423  for (depth = 0; depth < idx;)
424  {
425  while (*(++curr) != '[');
426  depth++;
427  }
428 
429  val = curr + 1;
430  while (*(++curr) != ']');
431  val = g_strndup(val, curr - val);
432 
433  return val;
434 }
435 
444 int fo_config_is_list(fo_conf* conf, char* group, char* key, GError** error)
445 {
446  GTree* tree;
447  char* val;
448 
449  if (!conf || conf->group_map == NULL)
450  throw_error(
451  error,
452  RETRIEVE_ERROR,
454  "ERROR: invalid fo_conf object passed to fo_config_is_list");
455 
456  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
457  throw_error(
458  error,
459  RETRIEVE_ERROR,
461  "ERROR: unknown group \"%s\"", group);
462 
463  if ((val = g_tree_lookup(tree, key)) == NULL)
464  throw_error(
465  error,
466  RETRIEVE_ERROR,
468  "ERROR: unknown key/value expression \"%s\"", key);
469 
470  return val[0] == '[';
471 }
472 
480 int fo_config_list_length(fo_conf* conf, char* group, char* key, GError** error)
481 {
482  char* val;
483  char* curr;
484  int count = 0;
485 
486  if (!fo_config_is_list(conf, group, key, error))
487  throw_error(
488  error,
489  RETRIEVE_ERROR,
491  "ERROR: %s[%s] must be of type list to get length", group, key);
492  if (error && *error)
493  return 0;
494 
495  val = g_tree_lookup(
496  g_tree_lookup(conf->group_map, group), key);
497 
498  for (curr = val; *curr; curr++)
499  if (*curr == '[')
500  count++;
501 
502  return count;
503 }
504 
512 {
513  if (!conf) return;
514  if (conf->group_map) g_tree_unref(conf->group_map);
515  if (conf->key_sets) g_tree_unref(conf->key_sets);
516  if (conf->group_set) g_free(conf->group_set);
517 
518  conf->group_map = NULL;
519  conf->key_sets = NULL;
520  conf->group_set = NULL;
521 
522  g_free(conf);
523 }
524 
536 void fo_config_join(fo_conf* dst, fo_conf* src, GError** error)
537 {
538  int ngroups, i;
539  char** groups = fo_config_group_set(src, &ngroups);
540  GTree* keys;
541 
542  /* before making any changes, check that there are no conflicts */
543  for (i = 0; i < ngroups; i++)
544  {
545  if (fo_config_has_group(dst, groups[i]))
546  {
547  g_set_error(error, RETRIEVE_ERROR, fo_invalid_join,
548  "Cannot join configuration with conflicting group \"%s\"", groups[i]);
549  return;
550  }
551  }
552 
553  /* join the two configurations */
554  for (i = 0; i < ngroups; i++)
555  {
556  keys = g_tree_lookup(src->group_map, groups[i]);
557  keys = g_tree_ref(keys);
558  g_tree_insert(dst->group_map, g_strdup(groups[i]), keys);
559  }
560 }
561 
562 /* ************************************************************************** */
563 /* *** special interface **************************************************** */
564 /* ************************************************************************** */
565 
577 char** fo_config_group_set(fo_conf* conf, int* length)
578 {
579  if (!conf)
580  {
581  *length = 0;
582  return NULL;
583  }
584 
585  if (conf->group_set)
586  {
587  *length = conf->n_groups;
588  return conf->group_set;
589  }
590 
591  if (conf->group_map == NULL)
592  {
593  *length = 0;
594  return NULL;
595  }
596 
597  *length = g_tree_nnodes(conf->group_map);
598  conf->n_groups = *length;
599  conf->group_set = g_new0(
600  char*, *length);
601  g_tree_foreach(conf->group_map, (GTraverseFunc) collect_keys, conf->group_set);
602 
603  return conf->group_set;
604 }
605 
619 char** fo_config_key_set(fo_conf* conf, char* group, int* length)
620 {
621  GTree* tree;
622  char** ret;
623  *length = 0;
624 
625  if (!conf)
626  return NULL;
627 
628  if (!conf->key_sets)
629  conf->key_sets = g_tree_new_full(str_comp, NULL, g_free, g_free);
630 
631  if (conf->group_map == NULL)
632  return NULL;
633 
634  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
635  return NULL;
636  *length = g_tree_nnodes(tree);
637 
638  if ((ret = g_tree_lookup(conf->key_sets, group)))
639  return ret;
640 
641  ret = g_new0(
642  char*, *length);
643  g_tree_foreach(tree, (GTraverseFunc) collect_keys, ret);
644  g_tree_insert(conf->key_sets, g_strdup(group), ret);
645 
646  return ret;
647 }
648 
656 int fo_config_has_group(fo_conf* conf, char* group)
657 {
658  if (conf == NULL)
659  return 0;
660  if (!conf->group_map)
661  return 0;
662  return g_tree_lookup(conf->group_map, group) != NULL;
663 }
664 
673 int fo_config_has_key(fo_conf* conf, char* group, char* key)
674 {
675  GTree* tree;
676 
677  if (conf == NULL)
678  return 0;
679  if (!conf->group_map)
680  return 0;
681  if ((tree = g_tree_lookup(conf->group_map, group)) == NULL)
682  return 0;
683  return g_tree_lookup(tree, key) != NULL;
684 }
685 
695 char* trim(char* ptext)
696 {
697  if (ptext && ptext[0])
698  {
699  int len = strlen(ptext);
700  while (isspace(ptext[len - 1])) ptext[--len] = 0; // right trim
701  while (isspace(*ptext)) ++ptext; // left trim
702  }
703 
704  return ptext;
705 }
706 
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:536
static gboolean fo_config_sub(const GMatchInfo *match, GString *ret, gpointer data)
Definition: fossconfig.c:129
int fo_config_has_group(fo_conf *conf, char *group)
Checks if the currently parsed configuration file has a specific group.
Definition: fossconfig.c:656
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:444
FOSSology library to read config file.
char ** group_set
Array of groups.
Definition: fossconfig.h:49
static const gchar * fo_conf_variable
Definition: fossconfig.c:67
Store the results of a regex match.
Definition: scanners.hpp:39
GTree * key_sets
Tree of sets of keys.
Definition: fossconfig.h:48
GRegex * fo_conf_parse
Regex for parsing.
Definition: fossconfig.c:69
GTree * group_map
Tree of groups in conf file.
Definition: fossconfig.h:47
static gboolean collect_keys(char *key, gpointer *value, char **data)
Definition: fossconfig.c:97
File is missing.
Definition: fossconfig.h:34
File is invalid.
Definition: fossconfig.h:39
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, the error object is set and NULL is returned.
Definition: fossconfig.c:341
char ** fo_config_group_set(fo_conf *conf, int *length)
Gets the set of group names.
Definition: fossconfig.c:577
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:159
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:673
#define BUFFER_SIZE
Maximum buffer length.
Definition: fossconfig.c:113
#define PARSE_ERROR
Definition: fossconfig.h:28
fo_conf * conf
The loaded configuration data.
Definition: fo_cli.c:50
Required group is missing.
Definition: fossconfig.h:35
static gint str_comp(gconstpointer a, gconstpointer b, gpointer user_data)
Definition: fossconfig.c:81
Requested group is invalid.
Definition: fossconfig.h:38
Required key is missing.
Definition: fossconfig.h:36
char * fo_config_get_list(fo_conf *conf, char *group, char *key, int idx, GError **error)
Definition: fossconfig.c:387
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:480
Requested key is invalid.
Definition: fossconfig.h:37
void fo_config_free(fo_conf *conf)
Frees the memory associated with the internal configuration data structures.
Definition: fossconfig.c:511
list_t type structure used to keep various lists. (e.g. there are multiple lists).
Definition: nomos.h:321
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:619
int n_groups
Number of groups.
Definition: fossconfig.h:50
#define throw_error(error, domain, code,...)
Definition: fossconfig.c:116
fo_conf * fo_config_load(char *rawname, GError **error)
Load the configuration information from the provided file.
Definition: fossconfig.c:280
GRegex * fo_conf_replace
Regex for replace.
Definition: fossconfig.c:70
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:214
Unable to load config.
Definition: fossconfig.h:41
Join is invalid.
Definition: fossconfig.h:40
static const gchar * fo_conf_pattern
Definition: fossconfig.c:56
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:695