Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcconfig.c
index 8731aa1dbd7b54c766b0640ddc5e029819e1e200..55524a32077ce3919cac0be5edfbe353dc7d6b08 100644 (file)
@@ -2,14 +2,13 @@
 
   silcconfig.c
 
-  Author: Johnny Mnemonic <johnny@themnemonic.org>
+  Author: Giovanni Giacobbi <giovanni@giacobbi.net>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 2002 - 2007 Giovanni Giacobbi
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
+  the Free Software Foundation; version 2 of the License.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,7 +18,7 @@
 */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.h"
 
 /* limit debug logging verbosity */
 #if 0
@@ -41,13 +40,14 @@ typedef struct SilcConfigOptionStruct {
 
 /* unique for each config file (and included ones) */
 struct SilcConfigFileObject {
-  char *filename; /* the original filename opened */
-  int level;   /* parsing level, how many nested silc_config_main we have */
-  char *base;  /* this is a fixed pointer to the base location */
-  char *p;     /* the Parser poitner */
-  uint32 len;  /* fixed length of the whole file */
-  uint32 line; /* current parsing line, strictly linked to p */
-  bool included; /* wether this file is main or included */
+  char *filename;      /* the original filename opened */
+  int level;           /* parsing level, how many nested
+                          silc_config_main we have */
+  char *base;          /* this is a fixed pointer to the base location */
+  char *p;             /* the Parser poitner */
+  SilcUInt32 len;      /* fixed length of the whole file */
+  SilcUInt32 line;     /* current parsing line, strictly linked to p */
+  SilcBool included;   /* wether this file is main or included */
 };
 
 /* We need the entity to base our block-style parsing on */
@@ -58,22 +58,23 @@ struct SilcConfigEntityObject {
 
 /* access error descriptions only with silc_config_strerror() */
 static char *errorstrs[] = {
-  "-OK",                                       /* SILC_CONFIG_OK */
-  "-SILENT",                                   /* SILC_CONFIG_ESILENT */
-  "Invalid syntax",                            /* SILC_CONFIG_EGENERIC */
-  "Internal error! Please report this bug",    /* SILC_CONFIG_EINTERNAL */
-  "Can't open specified file",                 /* SILC_CONFIG_ECANTOPEN */
-  "Expected open-brace '{'",                   /* SILC_CONFIG_EOPENBRACE */
-  "Missing close-brace '}'",                   /* SILC_CONFIG_ECLOSEBRACE */
-  "Invalid data type",                         /* SILC_CONFIG_ETYPE */
-  "Unknown option",                            /* SILC_CONFIG_EBADOPTION */
-  "Invalid text",                              /* SILC_CONFIG_EINVALIDTEXT */
-  "Double option specification",               /* SILC_CONFIG_EDOUBLE */
-  "Expected data but not found",               /* SILC_CONFIG_EEXPECTED */
-  "Expected '='",                              /* SILC_CONFIG_EEXPECTEDEQUAL */
-  "Unexpected data",                           /* SILC_CONFIG_EUNEXPECTED */
-  "Missing mandatory fields",                  /* SILC_CONFIG_EMISSFIELDS */
-  "Missing ';'",                               /* SILC_CONFIG_EMISSCOLON */
+  "-OK",                                     /* SILC_CONFIG_OK */
+  "-SILENT",                                 /* SILC_CONFIG_ESILENT */
+  "-PRINTLINE",                                      /* SILC_CONFIG_EPRINTLINE */
+  "Invalid syntax",                          /* SILC_CONFIG_EGENERIC */
+  "Internal error! Please report this bug",   /* SILC_CONFIG_EINTERNAL */
+  "Can't open specified file",               /* SILC_CONFIG_ECANTOPEN */
+  "Expected open-brace '{'",                 /* SILC_CONFIG_EOPENBRACE */
+  "Missing close-brace '}'",                 /* SILC_CONFIG_ECLOSEBRACE */
+  "Invalid data type",                       /* SILC_CONFIG_ETYPE */
+  "Unknown option",                          /* SILC_CONFIG_EBADOPTION */
+  "Invalid text",                            /* SILC_CONFIG_EINVALIDTEXT */
+  "Double option specification",             /* SILC_CONFIG_EDOUBLE */
+  "Expected data but not found",             /* SILC_CONFIG_EEXPECTED */
+  "Expected '='",                            /* SILC_CONFIG_EEXPECTEDEQUAL */
+  "Unexpected data",                         /* SILC_CONFIG_EUNEXPECTED */
+  "Missing mandatory fields",                /* SILC_CONFIG_EMISSFIELDS */
+  "Missing ';'",                             /* SILC_CONFIG_EMISSCOLON */
 };
 
 /* return string describing SilcConfig's error code */
@@ -93,18 +94,20 @@ char *silc_config_strerror(int errnum)
 static void my_trim_spaces(SilcConfigFile *file)
 {
   register char *r = file->p;
-  while (isspace(*r))
+  while ((*r != '\0' && *r != EOF) && isspace((int)*r))
     if (*r++ == '\n') file->line++;
   file->p = r;
 }
+
 /* Skips the current line until newline (lf or cr) */
 static void my_skip_line(SilcConfigFile *file)
 {
   register char *r = file->p;
-  while (*r && (*r != '\n') && (*r != '\r')) r++;
-  file->p = (*r ? r + 1 : r);
+  while ((*r != '\0' && *r != EOF) && (*r != '\n') && (*r != '\r')) r++;
+  file->p = ((*r != '\0' && *r != EOF) ? r + 1 : r);
   file->line++;
 }
+
 /* Obtains a text token from the current position until first separator.
  * a separator is any non alphanumeric character nor "_" or "-" */
 static char *my_next_token(SilcConfigFile *file, char *to)
@@ -112,12 +115,13 @@ static char *my_next_token(SilcConfigFile *file, char *to)
   register char *o;
   my_trim_spaces(file);
   o = file->p;
-  while (isalnum(*o) || (*o == '_') || (*o == '-'))
+  while (isalnum((int)*o) || (*o == '_') || (*o == '-'))
     *to++ = *o++;
   *to = '\0';
   file->p = o;
   return to;
 }
+
 /* Obtains a string from the current position. The only difference from
  * next_token() is that quoted-strings are also accepted */
 static char *my_get_string(SilcConfigFile *file, char *to)
@@ -145,7 +149,8 @@ static char *my_get_string(SilcConfigFile *file, char *to)
   /* we don't need quote parsing, fall-back to token extractor */
   my_next_token(file, to);
   return to;
-};
+}
+
 /* Skips all comment lines and spaces lines until first useful character */
 static void my_skip_comments(SilcConfigFile *file)
 {
@@ -170,29 +175,31 @@ static SilcConfigOption *silc_config_find_option(SilcConfigEntity ent,
   }
   return NULL;
 }
-/* ... */
+
+/* Converts a string in the type specified. returns a dynamically
+ * allocated pointer. */
 static void *silc_config_marshall(SilcConfigType type, const char *val)
 {
   void *pt;
   int val_int;
-  bool val_bool;
+  SilcBool val_boolean;
   char *val_tmp;
-  uint32 val_size;
+  SilcUInt32 val_size;
 
   switch (type) {
     case SILC_CONFIG_ARG_TOGGLE:
       if (!strcasecmp(val, "yes") || !strcasecmp(val, "true") ||
                !strcasecmp(val, "on") || !strcasecmp(val, "1")) {
-       val_bool = TRUE;
+       val_boolean = TRUE;
       }
       else if (!strcasecmp(val, "no") || !strcasecmp(val, "false") ||
                !strcasecmp(val, "off") || !strcasecmp(val, "0")) {
-       val_bool = FALSE;
+       val_boolean = FALSE;
       }
       else
        return NULL;
-      pt = silc_calloc(1, sizeof(val_bool));
-      *(bool *)pt = (bool) val_bool;
+      pt = silc_calloc(1, sizeof(val_boolean));
+      *(SilcBool *)pt = (SilcBool) val_boolean;
       return pt;
     case SILC_CONFIG_ARG_INT:
       val_int = (int) strtol(val, &val_tmp, 0);
@@ -202,21 +209,21 @@ static void *silc_config_marshall(SilcConfigType type, const char *val)
       *(int *)pt = val_int;
       return pt;
     case SILC_CONFIG_ARG_SIZE:
-      val_size = (uint32) strtol(val, &val_tmp, 0);
+      val_size = (SilcUInt32) strtol(val, &val_tmp, 0);
       if (val == val_tmp)
        return NULL; /* really wrong, there must be at least one digit */
       /* Search for a designator */
-      switch (tolower(val_tmp[0])) {
+      switch (tolower((int)val_tmp[0])) {
        case '\0': /* None */
          break;
        case 'k': /* Kilobytes */
-         val_size *= (uint32) 1024;
+         val_size *= (SilcUInt32) 1024;
          break;
        case 'm': /* Megabytes */
-         val_size *= (uint32) (1024 * 1024);
+         val_size *= (SilcUInt32) (1024 * 1024);
          break;
        case 'g':
-         val_size *= (uint32) (1024 * 1024 * 1024);
+         val_size *= (SilcUInt32) (1024 * 1024 * 1024);
          break;
        default:
          return NULL;
@@ -225,7 +232,7 @@ static void *silc_config_marshall(SilcConfigType type, const char *val)
       if (val_tmp[1])
        return NULL;
       pt = silc_calloc(1, sizeof(val_size));
-      *(uint32 *)pt = val_size;
+      *(SilcUInt32 *)pt = val_size;
       return pt;
     case SILC_CONFIG_ARG_STR: /* the only difference between STR and STRE is */
       if (!val[0])           /* that STR cannot be empty, while STRE can.  */
@@ -250,16 +257,16 @@ static void *silc_config_marshall(SilcConfigType type, const char *val)
 /* Tries to open the config file and returns a valid SilcConfigFile object
  * or NULL if failed */
 
-SilcConfigFile *silc_config_open(char *configfile)
+SilcConfigFile *silc_config_open(const char *configfile)
 {
   char *buffer;
-  uint32 filelen;
+  SilcUInt32 filelen;
   SilcConfigFile *ret;
 
-  if (!(buffer = silc_file_readfile(configfile, &filelen)))
+  if (!(buffer = silc_file_readfile(configfile, &filelen, NULL)))
     return NULL;
 
-  ret = (SilcConfigFile *) silc_calloc(1, sizeof(*ret));
+  ret = silc_calloc(1, sizeof(*ret));
   ret->filename = strdup(configfile);
   ret->base = ret->p = buffer;
   ret->len = filelen;
@@ -272,14 +279,6 @@ SilcConfigFile *silc_config_open(char *configfile)
 void silc_config_close(SilcConfigFile *file)
 {
   if (file) {
-    /* XXX FIXME: this check could probably be removed later */
-    uint32 my_len = (uint32) (strchr(file->base, EOF) - file->base);
-    SILC_CONFIG_DEBUG(("file=0x%x name=\"%s\" level=%d line=%lu", (uint32) file,
-                       file->filename, file->level, file->line));
-    if (my_len != file->len) {
-      fprintf(stderr, "FATAL ERROR: saved len and current len does not match!\n");
-      abort();
-    }
     silc_free(file->filename);
     memset(file->base, 'F', file->len);
     silc_free(file->base);
@@ -293,13 +292,15 @@ void silc_config_close(SilcConfigFile *file)
 SilcConfigEntity silc_config_init(SilcConfigFile *file)
 {
   SilcConfigEntity ret;
+
   if (!file)
     return NULL;
+
   SILC_CONFIG_DEBUG(("Allocating new config entity"));
-  ret = (SilcConfigEntity) silc_calloc(1, sizeof(*ret));
+  ret = silc_calloc(1, sizeof(*ret));
   ret->file = file;
   return ret;
-};
+}
 
 /* Returns the original filename of the object file */
 
@@ -312,7 +313,7 @@ char *silc_config_get_filename(SilcConfigFile *file)
 
 /* Returns the current line that file parsing arrived at */
 
-uint32 silc_config_get_line(SilcConfigFile *file)
+SilcUInt32 silc_config_get_line(SilcConfigFile *file)
 {
   if (file)
     return file->line;
@@ -322,11 +323,11 @@ uint32 silc_config_get_line(SilcConfigFile *file)
 /* Returns a pointer to the beginning of the requested line.  If the line
  * was not found, NULL is returned */
 
-char *silc_config_read_line(SilcConfigFile *file, uint32 line)
+char *silc_config_read_line(SilcConfigFile *file, SilcUInt32 line)
 {
   register char *p;
   int len;
-  char *ret, *endbuf;
+  char *ret = NULL, *endbuf;
 
   if (!file || (line <= 0))
     return NULL;
@@ -341,13 +342,10 @@ char *silc_config_read_line(SilcConfigFile *file, uint32 line)
  found:
   if ((endbuf = strchr(p, '\n'))) {
     len = endbuf - p;
-    ret = silc_calloc(len, sizeof(*ret));
-    strncpy(ret, p, len);
-    ret[len] = '\0';
-  }
-  else {
-    ret = silc_calloc(strlen(p), sizeof(*ret));
-    strcpy(ret, p);
+    if (len > 0)
+      ret = silc_memdup(p, len);
+  } else {
+    ret = silc_memdup(p, strlen(p));
   }
   return ret;
 }
@@ -361,11 +359,16 @@ char *silc_config_read_current_line(SilcConfigFile *file)
 
 /* (Private) destroy a SilcConfigEntity */
 
-static void silc_config_destroy(SilcConfigEntity ent)
+static void silc_config_destroy(SilcConfigEntity ent, SilcBool destroy_opts)
 {
   SilcConfigOption *oldopt, *nextopt;
   SILC_CONFIG_DEBUG(("Freeing config entity [ent=0x%x] [opts=0x%x]",
-                       (uint32) ent, (uint32) ent->opts));
+                       (SilcUInt32) ent, (SilcUInt32) ent->opts));
+
+  /* if she wants to preserve options just free the object struct */
+  if (!destroy_opts)
+    goto skip_sect;
+
   for (oldopt = ent->opts; oldopt; oldopt = nextopt) {
     nextopt = oldopt->next;
     memset(oldopt->name, 'F', strlen(oldopt->name) + 1);
@@ -373,6 +376,8 @@ static void silc_config_destroy(SilcConfigEntity ent)
     memset(oldopt, 'F', sizeof(*oldopt));
     silc_free(oldopt);
   }
+
+ skip_sect:
   memset(ent, 'F', sizeof(*ent));
   silc_free(ent);
 }
@@ -380,13 +385,14 @@ static void silc_config_destroy(SilcConfigEntity ent)
 /* Registers a new option in the specified entity.
  * Returns TRUE on success, FALSE if already registered. */
 
-bool silc_config_register(SilcConfigEntity ent, const char *name,
+SilcBool silc_config_register(SilcConfigEntity ent, const char *name,
                          SilcConfigType type, SilcConfigCallback cb,
                          const SilcConfigTable *subtable, void *context)
 {
   SilcConfigOption *newopt;
-  SILC_CONFIG_DEBUG(("Register new option=\"%s\" type=%u cb=0x%08x context=0x%08x",
-               name, type, (uint32) cb, (uint32) context));
+  SILC_CONFIG_DEBUG(("Register new option=\"%s\" "
+                    "type=%u cb=0x%08x context=0x%08x",
+                    name, type, (SilcUInt32) cb, (SilcUInt32) context));
 
   /* if we are registering a block, make sure there is a specified sub-table */
   if (!ent || !name || ((type == SILC_CONFIG_ARG_BLOCK) && !subtable))
@@ -403,7 +409,7 @@ bool silc_config_register(SilcConfigEntity ent, const char *name,
   }
 
   /* allocate and append the new option */
-  newopt = (SilcConfigOption *) silc_calloc(1, sizeof(*newopt));
+  newopt = silc_calloc(1, sizeof(*newopt));
   newopt->name = strdup(name);
   newopt->type = type;
   newopt->cb = cb;
@@ -423,7 +429,7 @@ bool silc_config_register(SilcConfigEntity ent, const char *name,
 
 /* Register a new option table in the specified config entity */
 
-bool silc_config_register_table(SilcConfigEntity ent,
+SilcBool silc_config_register_table(SilcConfigEntity ent,
                                const SilcConfigTable table[], void *context)
 {
   int i;
@@ -471,7 +477,8 @@ static int silc_config_main_internal(SilcConfigEntity ent)
 
     /* obtain the keyword */
     my_next_token(file, buf);
-    SILC_CONFIG_DEBUG(("Looking up keyword=\"%s\" [line=%lu]", buf, file->line));
+    SILC_CONFIG_DEBUG(("Looking up keyword=\"%s\" [line=%lu]",
+                      buf, file->line));
 
     /* handle special directive */
     if (!strcasecmp(buf, "include")) {
@@ -576,7 +583,7 @@ static int silc_config_main_internal(SilcConfigEntity ent)
     }
     else {
       void *pt;
-      int ret;
+      int ret = 0;     /* very important in case of no cb */
 
       if (*(*p)++ != '=')
        return SILC_CONFIG_EEXPECTEDEQUAL;
@@ -592,15 +599,17 @@ static int silc_config_main_internal(SilcConfigEntity ent)
       pt = silc_config_marshall(thisopt->type, buf);
       if (!pt)
        return SILC_CONFIG_EINVALIDTEXT;
-      if (thisopt->cb) {
+      if (thisopt->cb)
        ret = thisopt->cb(thisopt->type, thisopt->name, file->line,
                          pt, thisopt->context);
-       if (ret) {
-         SILC_CONFIG_DEBUG(("Callback refused the value [ret=%d]", ret));
-         return ret;
-       }
-      }
+
+      /* since we have to free "pt" both on failure and on success, we
+         assume that ret == 0 if we didn't actually call any cb. */
       silc_free(pt);
+      if (ret) {
+       SILC_CONFIG_DEBUG(("Callback refused the value [ret=%d]", ret));
+       return ret;
+      }
     }
     continue;
 
@@ -639,7 +648,9 @@ int silc_config_main(SilcConfigEntity ent)
    * destroyed. */
  main_cleanup:
   if ((file->level != 0) || (file->included != TRUE))
-    silc_config_destroy(ent);
+    silc_config_destroy(ent, TRUE);
+  else
+    silc_config_destroy(ent, FALSE);
 
  main_end:
   return ret;