+static void silc_config_destroy(SilcConfigEntity ent)
+{
+ SilcConfigOption *oldopt, *nextopt;
+ SILC_CONFIG_DEBUG(("Freeing config entity [ent=0x%x] [opts=0x%x]",
+ (uint32) ent, (uint32) ent->opts));
+ for (oldopt = ent->opts; oldopt; oldopt = nextopt) {
+ nextopt = oldopt->next;
+ memset(oldopt->name, 'F', strlen(oldopt->name) + 1);
+ silc_free(oldopt->name);
+ memset(oldopt, 'F', sizeof(*oldopt));
+ silc_free(oldopt);
+ }
+ memset(ent, 'F', sizeof(*ent));
+ silc_free(ent);
+}
+
+/* Registers a new option in the specified entity */
+
+void 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));
+
+ if (!ent || !name)
+ return;
+ /* if we are registering a block, make sure there is a specified sub-table */
+ if ((type == SILC_CONFIG_ARG_BLOCK) && !subtable)
+ return;
+ /* refuse special tag */
+ if (!strcasecmp(name, "include"))
+ return;
+ if (silc_config_find_option(ent, name)) {
+ fprintf(stderr, "Internal Error: Option double registered\n");
+ abort();
+ }
+
+ /* allocate and append the new option */
+ newopt = (SilcConfigOption *) silc_calloc(1, sizeof(*newopt));
+ newopt->name = strdup(name);
+ newopt->type = type;
+ newopt->cb = cb;
+ newopt->subtable = subtable;
+ newopt->context = context;
+
+ if (!ent->opts)
+ ent->opts = newopt;
+ else {
+ SilcConfigOption *tmp;
+ for (tmp = ent->opts; tmp->next; tmp = tmp->next);
+ tmp->next = newopt;
+ }
+}
+
+/* Register a new option table in the specified config entity */
+
+void silc_config_register_table(SilcConfigEntity ent,
+ const SilcConfigTable table[], void *context)
+{
+ int i;
+ if (!ent || !table) return;
+ SILC_CONFIG_DEBUG(("Registering table"));
+ /* FIXME: some potability checks needed */
+ for (i = 0; table[i].name; i++) {
+ silc_config_register(ent, table[i].name, table[i].type,
+ table[i].callback, table[i].subtable, context);
+ }
+}
+
+/* ... */
+
+static int silc_config_main_internal(SilcConfigEntity ent)
+{
+ SilcConfigFile *file = ent->file;
+ char **p = &file->p;
+
+ /* loop throught statements */
+ while (1) {
+ char buf[255];
+ SilcConfigOption *thisopt;
+
+ /* makes it pointing to the next interesting char */
+ my_skip_comments(file);
+ /* got eof? */
+ if (**p == '\0' || **p == EOF) {
+ if (file->level > 1) /* cannot get eof in a sub-level! */
+ return SILC_CONFIG_EEXPECTED;
+ goto finish;
+ }
+ /* check if we completed this (sub) section (it doesn't matter if this
+ * is the main section) */
+ if (**p == '}') {
+ if (file->level < 2) /* can't be! must be at least one sub-block */
+ return SILC_CONFIG_EUNEXPECTED;
+ (*p)++;
+ goto finish;
+ }
+ //SILC_LOG_HEXDUMP(("Preparing lookup at line=%lu", file->line), *p, 16);
+
+ /* obtain the keyword */
+ my_next_token(file, buf);
+ SILC_CONFIG_DEBUG(("Looking up keyword=\"%s\" [line=%lu]", buf, file->line));
+
+ /* handle special directive */
+ if (!strcasecmp(buf, "include")) {
+ int ret;
+ SilcConfigFile *inc_file;
+ SilcConfigEntity inc_ent;
+
+ my_trim_spaces(file); /* prepare next char */
+
+ /* Now trying to include the specified file. The included file will
+ * be allowed to include sub-files but it will preserve the block-level
+ * of the including block. Note that the included file won't be allowed
+ * to raise the block level of the including block. */
+
+ my_get_string(file, buf); /* get the filename */
+ SILC_LOG_DEBUG(("Including file \"%s\"", buf));
+ /* before getting on, check if this row is REALLY complete */
+ if (*(*p)++ != ';')
+ return SILC_CONFIG_EMISSCOLON;
+
+ /* open the file and start the parsing */
+ inc_file = silc_config_open(buf);
+ if (!inc_file) /* does it point a valid filename? */
+ return SILC_CONFIG_ECANTOPEN;
+ inc_file->included = TRUE;
+
+ /* create a new entity and hack it to use the same options */
+ inc_ent = silc_config_init(inc_file);
+ inc_ent->opts = ent->opts;
+ ret = silc_config_main(inc_ent);
+
+ /* Cleanup.
+ * If the included file returned an error, the application will probably
+ * want to output some kind of error message. Because of this, we can't
+ * destroy THIS file object. The hack works this way: The application
+ * expects to destroy the originally created object file, so we'll swap
+ * the original file with the included file. */
+ if (ret) {
+ SilcConfigFile tmp_file;
+ SILC_CONFIG_DEBUG(("SWAPPING FILE OBJECTS"));
+ memcpy(&tmp_file, inc_file, sizeof(tmp_file));
+ memcpy(inc_file, file, sizeof(tmp_file));
+ silc_config_close(inc_file);
+ memcpy(file, &tmp_file, sizeof(tmp_file));
+ return ret;
+ }
+ /* otherwise if no errors encoured, continue normally */
+ silc_config_close(inc_file);
+ continue; /* this one is handled */