+/* limit debug logging verbosity */
+#if 0
+#define SILC_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SILC_CONFIG_DEBUG(fmt)
+#endif
+
+/* this is the option struct and currently it is only used internally to
+ * the module and other structs. */
+typedef struct SilcConfigOptionStruct {
+ char *name; /* *lowercase* name of the option */
+ SilcConfigType type; /* important: the type of the returned value */
+ SilcConfigCallback cb; /* the value handler */
+ const SilcConfigTable *subtable; /* used if type is SILC_CONFIG_ARG_BLOCK */
+ void *context; /* context passed to the callback function */
+ struct SilcConfigOptionStruct *next;
+} SilcConfigOption;
+
+/* 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 */
+ SilcUInt32 len; /* fixed length of the whole file */
+ SilcUInt32 line; /* current parsing line, strictly linked to p */
+ bool included; /* wether this file is main or included */
+};
+
+/* We need the entity to base our block-style parsing on */
+struct SilcConfigEntityObject {
+ SilcConfigOption *opts; /* known options list */
+ SilcConfigFile *file; /* parsing file object */
+};
+
+/* access error descriptions only with silc_config_strerror() */
+static char *errorstrs[] = {
+ "-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 */
+char *silc_config_strerror(int errnum)
+{
+ if ((errnum < 0) || (errnum >= sizeof(errorstrs)/sizeof(*errorstrs)) ||
+ (errorstrs[errnum] == NULL)) {
+ char *defret = "-INVALIDERROR";
+ return defret;
+ }
+ return errorstrs[errnum];
+}
+
+/* Begin of internal SilcConfig's text util functions */
+
+/* Points the first non-space character */
+static void my_trim_spaces(SilcConfigFile *file)
+{
+ register char *r = file->p;
+ while ((*r != '\0' && *r != EOF) && isspace(*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 != '\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)
+{
+ register char *o;
+ my_trim_spaces(file);
+ o = file->p;
+ while (isalnum(*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)
+{
+ char *o;
+ my_trim_spaces(file);
+ o = file->p;
+ if (*o == '"') {
+ char *quot = strchr(++o, '"');
+ int len = quot - o;
+ if (!quot) { /* XXX FIXME: gotta do something here */
+ printf("Bullshit, missing matching \"");
+ exit(1);
+ }
+ if (len <= 0)
+ *to = '\0';
+ else {
+ strncpy(to, o, len);
+ to[len] = '\0';
+ }
+ /* update stream pointer */
+ file->p = quot + 1;
+ return 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)
+{
+ while (1) {
+ my_trim_spaces(file);
+ if (*file->p != '#') return;
+ my_skip_line(file);
+ }
+}
+
+/* End of internal text functions
+ * Next section contains SilcConfig internal config utils */
+
+/* find an option in the list by name and returns its pointer */
+static SilcConfigOption *silc_config_find_option(SilcConfigEntity ent,
+ const char *name)
+{
+ SilcConfigOption *tmp;
+ for (tmp = ent->opts; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, name))
+ return tmp;
+ }
+ 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;
+ char *val_tmp;
+ 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;
+ }
+ else if (!strcasecmp(val, "no") || !strcasecmp(val, "false") ||
+ !strcasecmp(val, "off") || !strcasecmp(val, "0")) {
+ val_bool = FALSE;
+ }
+ else
+ return NULL;
+ pt = silc_calloc(1, sizeof(val_bool));
+ *(bool *)pt = (bool) val_bool;
+ return pt;
+ case SILC_CONFIG_ARG_INT:
+ val_int = (int) strtol(val, &val_tmp, 0);
+ if (*val_tmp) /* error converting string */
+ return NULL;
+ pt = silc_calloc(1, sizeof(val_int));
+ *(int *)pt = val_int;
+ return pt;
+ case SILC_CONFIG_ARG_SIZE:
+ 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])) {
+ case '\0': /* None */
+ break;
+ case 'k': /* Kilobytes */
+ val_size *= (SilcUInt32) 1024;
+ break;
+ case 'm': /* Megabytes */
+ val_size *= (SilcUInt32) (1024 * 1024);
+ break;
+ case 'g':
+ val_size *= (SilcUInt32) (1024 * 1024 * 1024);
+ break;
+ default:
+ return NULL;
+ }
+ /* the string must die here */
+ if (val_tmp[1])
+ return NULL;
+ pt = silc_calloc(1, sizeof(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. */
+ return NULL;
+ case SILC_CONFIG_ARG_STRE:
+ pt = (void *) strdup(val);
+ return pt;
+ /* following types are not supposed to have a return value */
+ case SILC_CONFIG_ARG_BLOCK:
+ case SILC_CONFIG_ARG_NONE:
+ return NULL;
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/* End of internal functions */
+