/* xmlrc.c
 * Copyright (C) 2002-2004 Pascal Eberhard <pascal.ebo@netcourrier.com>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#include "common.h"

#include <stdio.h>      /* define FILENAME_MAX */
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>


#include "xmlrcinline.inc"

// ----------------------------------------------------------------------------
// .apwalrc.xml file:
//   apwalrc
//     apps
//       app
//       ...
//     iconsel_pref
//       icon_dirs
//         path
//         ...
//       file_exts
//         extension
//         ..
//     apwal_pref
//       
//
// ----------------------------------------------------------------------------

// only access filerc by xmlrc_set_resource_file/xmlrc_resource_file
static char g_xmlrc_filerc[FILENAME_MAX] = { 0 };

static char*    xmlrc_resource_file(void);

static app_list_t     * xmlrc_parse_apps(xmlDocPtr doc, xmlNodePtr cur);
static app_t          * xmlrc_parse_app(xmlDocPtr doc, xmlNodePtr cur);

static apwal_pref_t   * xmlrc_parse_apwal_pref(xmlDocPtr doc, xmlNodePtr cur);

static iconsel_pref_t * xmlrc_parse_iconsel_pref(xmlDocPtr doc, xmlNodePtr cur);
static GList          * xmlrc_parse_icon_dirs(xmlDocPtr doc, xmlNodePtr cur);
static icon_dir_t     * xmlrc_parse_icon_dir(xmlDocPtr doc, xmlNodePtr cur);

static GList          * xmlrc_parse_file_exts(xmlDocPtr doc, xmlNodePtr cur);
static file_ext_t     * xmlrc_parse_file_ext(xmlDocPtr doc, xmlNodePtr cur);


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void xmlrc_load_from_file(app_list_t **apps, iconsel_pref_t **iconsel_pref,
                          apwal_pref_t **apwal_pref)
{
  xmlDocPtr    doc;
  xmlNodePtr   cur;
  gboolean     apps_is_loaded = FALSE;
  gboolean     iconsel_pref_is_loaded = FALSE;
  gboolean     apwal_pref_is_loaded = FALSE;

  g_assert(apps != NULL && apwal_pref != NULL); // iconsel_pref NULL in launcher

  LIBXML_TEST_VERSION
  xmlKeepBlanksDefault(0);

  // load tree from file
  doc = xmlParseFile(xmlrc_resource_file());
  if (doc == NULL)
    ERR("xmlParseFile, file:%s invalid", xmlrc_resource_file());

  cur = xmlDocGetRootElement(doc);
  if (cur == NULL)
    ERR("xmlDocGetRootElement, empty document, file:%s", xmlrc_resource_file());

  if (xmlStrcmp(cur->name, (const xmlChar *) "apwalrc"))
    ERR("xmlStrcmp, doc root:%s != apwalrc", cur->name);

  // load app list from tree
  cur = cur->xmlChildrenNode;
  while(cur != NULL)
  {
    if (xmlIsBlankNode(cur))
    {
      ERR("%s", "I just would like to know what is a xmlIsBlankNode");
    }
    if (!xmlStrcmp(cur->name, (const xmlChar *) "apps"))
    {
      if (apps_is_loaded == TRUE)
        ERR("%s", "there are more that one <apps> node");
      *apps = xmlrc_parse_apps(doc, cur);
      apps_is_loaded = TRUE;

    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *) "iconsel_pref"))
    {
      if (iconsel_pref_is_loaded == TRUE)
        ERR("%s", "there are more that one <iconsel_pref> node");
      if (iconsel_pref != NULL) // the launcher will not ask for it
        *iconsel_pref = xmlrc_parse_iconsel_pref(doc, cur);
      iconsel_pref_is_loaded = TRUE;
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *) "apwal_pref"))
    {
      if (apwal_pref_is_loaded == TRUE)
        ERR("%s", "there are more that one <apwal_pref> node");
      if (apwal_pref != NULL) // the launcher will not ask for it
        *apwal_pref = xmlrc_parse_apwal_pref(doc, cur);
      apwal_pref_is_loaded = TRUE;
    }
    else
      ERR("cur->name:%s invalid", cur->name);
    cur = cur->next;
  } //end while cur

  if (apps_is_loaded == FALSE)
    ERR("apps section not found in rc file:%s", xmlrc_resource_file());
  if (iconsel_pref_is_loaded == FALSE)
    ERR("iconsel_pref section not found in rc file:%s", xmlrc_resource_file());
  if (apwal_pref_is_loaded == FALSE)
    ERR("apwal_pref section not found in rc file:%s", xmlrc_resource_file());

  // free tree
  xmlFreeDoc(doc);
  xmlCleanupParser();
}

// ----------------------------------------------------------------------------
static app_list_t * xmlrc_parse_apps(xmlDocPtr doc, xmlNodePtr cur)
{
  app_list_t *apps;
  app_t      *app;
  apps = app_list_new();
  cur = cur->xmlChildrenNode;
  while (cur)
  {
    if (xmlStrcmp(cur->name, (const xmlChar *) "app"))
      ERR("cur->name:%s unknown", cur->name);
    app = xmlrc_parse_app(doc, cur);
    if (app != NULL)
      app_list_add(apps, app);
    cur = cur->next;
  }
  return apps;
}
// ----------------------------------------------------------------------------
static app_t * xmlrc_parse_app(xmlDocPtr doc, xmlNodePtr cur)
{
  app_t *app;
  gchar *cmdline = NULL;
  gchar *exec = NULL;
  gchar *args = NULL;
  gchar *path = NULL;
  gchar *icon = NULL;
  gint   x = 0;
  gint   y = 0;
  gchar *buf;

  cur = cur->xmlChildrenNode;
  while (cur != NULL)
  {
    if (!xmlStrcmp(cur->name, (const xmlChar *)"cmdline"))
      cmdline = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"name"))
    {
      // name property not used anymore starting from the v0.4.3
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"exec"))
      exec = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"args"))
      args = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"path"))
      path = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"icon"))
      icon = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"x"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      x = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"y"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      y = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else
      ERR("cur->name:%s unknown", cur->name);
    cur = cur->next;
  }

  // starting from the v0.4.3 cmdline replace exec and args
  if (cmdline == NULL)
  {
    if (exec == NULL)
    {
      if (args != NULL)
        cmdline = args;
      else
        cmdline = g_strdup("");
    }
    else if (args == NULL)
      cmdline = exec;
    else if (strcmp(args, "") == 0)
    {
      cmdline = exec;
      g_free(args);
    }
    else
    {
      cmdline = g_strconcat(exec, " ", args, NULL);
      g_free(exec);
      g_free(args);
    }
    
  }
  if (path == NULL)
    path = g_strdup("");
  if (icon == NULL)
    icon = g_strdup("");

  app = app_new(cmdline, path, icon, x, y);
  app_dumpln(app);
  return app;
}
// ----------------------------------------------------------------------------
static apwal_pref_t * xmlrc_parse_apwal_pref(xmlDocPtr doc, xmlNodePtr cur)
{
  apwal_pref_t *apwal_pref;
  gint      timeout = 1000;
  gint      exit_at_app_launch = FALSE;
  gboolean  activate_tooltips = TRUE;
  gboolean  iconsel_in_a_separate_window = TRUE;
  gboolean  iconsel_modal = FALSE;
  char     *buf;

  cur = cur->xmlChildrenNode;
  while (cur != NULL)
  {
    if (!xmlStrcmp(cur->name, (const xmlChar *)"timeout"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      timeout = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    } else if (!xmlStrcmp(cur->name, (const xmlChar *)"exit_at_app_launch"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      exit_at_app_launch = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"activate_tooltips"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      activate_tooltips = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name,
                        (const xmlChar *)"iconsel_in_a_separate_window"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      iconsel_in_a_separate_window = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"iconsel_modal"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      iconsel_modal = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else
      ERR("cur->name:%s unknown", cur->name);

    cur = cur->next;
  }//end while cur

  apwal_pref = apwal_pref_new(timeout, exit_at_app_launch, activate_tooltips,
                              iconsel_in_a_separate_window, iconsel_modal);
  return apwal_pref;
}
// ----------------------------------------------------------------------------
static iconsel_pref_t * xmlrc_parse_iconsel_pref(xmlDocPtr doc, xmlNodePtr cur)
{
  iconsel_pref_t *iconsel_pref;
  GList    *icon_dirs = NULL;
  GList    *file_exts = NULL;
  gboolean  select_48 = TRUE;
  gboolean  select_lt48 = TRUE;
  gboolean  select_gt48 = TRUE;
  gboolean  sort_mode = ICONSEL_SORT_DEFAULT;
  char     *buf;
  gboolean icon_dirs_are_loaded = FALSE;
  gboolean file_exts_are_loaded = FALSE;

  cur = cur->xmlChildrenNode;
  while (cur != NULL)
  {
    if (!xmlStrcmp(cur->name, (const xmlChar *) "icon_dirs"))
    {
      if (icon_dirs_are_loaded == TRUE)
        ERR("%s", "there are more that one <icon_dirs> node");
      icon_dirs = xmlrc_parse_icon_dirs(doc, cur);
      icon_dirs_are_loaded = TRUE;
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *) "file_exts"))
    {
      if (file_exts_are_loaded == TRUE)
        ERR("%s", "there are more that one <file_exts> node");
      file_exts = xmlrc_parse_file_exts(doc, cur);
      file_exts_are_loaded = TRUE;
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"select_48"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      select_48 = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"select_lt48"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      select_lt48 = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"select_gt48"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      select_gt48 = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"sort_mode"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      sort_mode = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else
      ERR("cur->name:%s unknown", cur->name);

    cur = cur->next;
  }//end while cur

  iconsel_pref = iconsel_pref_new(icon_dirs, file_exts, select_48,
                                  select_lt48, select_gt48, sort_mode);
  return iconsel_pref;
}
// ----------------------------------------------------------------------------
static GList * xmlrc_parse_icon_dirs(xmlDocPtr doc, xmlNodePtr cur)
{
  GList      *icon_dirs;
  icon_dir_t *icon_dir;

  icon_dirs = NULL;
  cur = cur->xmlChildrenNode;
  while (cur)
  {
    if (xmlStrcmp(cur->name, (const xmlChar *) "icon_dir"))
      ERR("cur->name:%s unknown", cur->name);
    icon_dir = xmlrc_parse_icon_dir(doc, cur);
    if (icon_dir != NULL)
      icon_dirs = g_list_append(icon_dirs, icon_dir);

    cur = cur->next;
  }
  return icon_dirs;
}
// ----------------------------------------------------------------------------
static icon_dir_t * xmlrc_parse_icon_dir(xmlDocPtr doc, xmlNodePtr cur)
{
  icon_dir_t *icon_dir;
  char       *path = NULL;
  gboolean   selected = 1 ;
  gboolean   recursive = FALSE;
  char      *buf;

  cur = cur->xmlChildrenNode;
  while (cur != NULL)
  {
    if (!xmlStrcmp(cur->name, (const xmlChar *) "path"))
      path = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"selected"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      selected = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"recursive"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      recursive = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else
      ERR("cur->name:%s unknown", cur->name);

    cur = cur->next;
  }//end while cur

  make_path_uniq(&path);
  icon_dir = icon_dir_new(path, selected, recursive);

  return icon_dir;
}
// ----------------------------------------------------------------------------
static GList * xmlrc_parse_file_exts(xmlDocPtr doc, xmlNodePtr cur)
{
  GList      *file_exts;
  file_ext_t *file_ext;

  file_exts = NULL;
  cur = cur->xmlChildrenNode;
  while (cur)
  {
    if (xmlStrcmp(cur->name, (const xmlChar *) "file_ext"))
      ERR("cur->name:%s unknown", cur->name);
    file_ext = xmlrc_parse_file_ext(doc, cur);
    if (file_ext != NULL)
      file_exts = g_list_append(file_exts, file_ext);

    cur = cur->next;
  }
  return file_exts;
}
// ----------------------------------------------------------------------------
static file_ext_t * xmlrc_parse_file_ext(xmlDocPtr doc, xmlNodePtr cur)
{
  file_ext_t *file_ext;
  char       *extension = NULL;
  gboolean   selected = 1 ;
  char      *buf;

  cur = cur->xmlChildrenNode;
  while (cur != NULL)
  {
    if (!xmlStrcmp(cur->name, (const xmlChar *) "extension"))
      extension = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    else if (!xmlStrcmp(cur->name, (const xmlChar *)"selected"))
    { buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
      selected = strtol(buf, (char **)NULL, 10);
      g_free(buf);
    }
    else
      ERR("cur->name:%s unknown", cur->name);

    cur = cur->next;
  }//end while cur

  file_ext = file_ext_new(extension, selected);
  return file_ext;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void xmlrc_save_to_file(app_list_t *apps, iconsel_pref_t *iconsel_pref,
                        apwal_pref_t *apwal_pref)
{
  xmlDocPtr   doc;
  xmlNodePtr  node1;
  xmlNodePtr  node2;
  xmlNodePtr  node3;
  app_t      *app;
  GList      *hamster;
  icon_dir_t *icon_dir;
  file_ext_t *file_ext;
  gint        cc;
  char        buf[20];
  char        flast[FILENAME_MAX];
  
  g_assert(apps != NULL && iconsel_pref != NULL && apwal_pref != NULL);

  // create new XML document
  doc = xmlNewDoc("1.0");
  doc->xmlRootNode = xmlNewDocNode(doc, NULL, "apwalrc", NULL);
  node1 = xmlNewChild(doc->xmlRootNode, NULL, "apps", NULL);
  
  // save the list of applications
  for (app=app_list_first(apps); app!=NULL; app=app_list_next(apps))
  {
    node2 = xmlNewChild(node1, NULL, "app", NULL);
    xmlNewChild(node2, NULL, "cmdline", app->cmdline);
    xmlNewChild(node2, NULL, "path", app->path);
    xmlNewChild(node2, NULL, "icon", app->icon);
    snprintf(buf, sizeof(buf), "%i", app->x);
    xmlNewChild(node2, NULL, "x", buf);
    snprintf(buf, sizeof(buf), "%i", app->y);
    xmlNewChild(node2, NULL, "y", buf);
  }

  // save the icon selection prefence
  node1 = xmlNewChild(doc->xmlRootNode, NULL, "iconsel_pref", NULL);
  snprintf(buf, sizeof(buf), "%i", iconsel_pref->select_48);
  xmlNewChild(node1, NULL, "select_48", buf);
  snprintf(buf, sizeof(buf), "%i", iconsel_pref->select_lt48);
  xmlNewChild(node1, NULL, "select_lt48", buf);
  snprintf(buf, sizeof(buf), "%i", iconsel_pref->select_gt48);
  xmlNewChild(node1, NULL, "select_gt48", buf);
  snprintf(buf, sizeof(buf), "%i", iconsel_pref->sort_mode);
  xmlNewChild(node1, NULL, "sort_mode", buf);
  node2 = xmlNewChild(node1, NULL, "icon_dirs", NULL);
  hamster = iconsel_pref->icon_dirs;
  while(hamster != NULL)
  {
    icon_dir = hamster->data;
    // if the icon directory is empty it is not to save
    if (strcmp(icon_dir->path, "") == 0)
    {
      hamster = g_list_next(hamster);
      continue;
    }
    node3 = xmlNewChild(node2, NULL, "icon_dir", NULL);
    xmlNewChild(node3, NULL, "path", icon_dir->path);
    snprintf(buf, sizeof(buf), "%i", icon_dir->selected);
    xmlNewChild(node3, NULL, "selected", buf);
    snprintf(buf, sizeof(buf), "%i", icon_dir->recursive);
    xmlNewChild(node3, NULL, "recursive", buf);
    hamster = g_list_next(hamster);
  }
  node2 = xmlNewChild(node1, NULL, "file_exts", NULL);
  hamster = iconsel_pref->file_exts;
  while(hamster != NULL)
  {
    file_ext = hamster->data;
    // if the file name extension is empty it is not to save
    if (strcmp(file_ext->extension, "") == 0)
    {
      hamster = g_list_next(hamster);
      continue;
    }
    node3 = xmlNewChild(node2, NULL, "file_ext", NULL);
    xmlNewChild(node3, NULL, "extension", file_ext->extension);
    snprintf(buf, sizeof(buf), "%i", file_ext->selected);
    xmlNewChild(node3, NULL, "selected", buf);
    hamster = g_list_next(hamster);
  }

  // save the apwal preferences
  node1 = xmlNewChild(doc->xmlRootNode, NULL, "apwal_pref", NULL);

  snprintf(buf, sizeof(buf), "%i", apwal_pref->timeout);
  xmlNewChild(node1, NULL, "timeout", buf);

  snprintf(buf, sizeof(buf), "%i", apwal_pref->exit_at_app_launch);
  xmlNewChild(node1, NULL, "exit_at_app_launch", buf);

  snprintf(buf, sizeof(buf), "%i", apwal_pref->activate_tooltips);
  xmlNewChild(node1, NULL, "activate_tooltips", buf);

  snprintf(buf, sizeof(buf), "%i", apwal_pref->iconsel_in_a_separate_window);
  xmlNewChild(node1, NULL, "iconsel_in_a_separate_window", buf);

  snprintf(buf, sizeof(buf), "%i", apwal_pref->iconsel_modal);
  xmlNewChild(node1, NULL, "iconsel_modal", buf);

  // save XML file
  strcpy(flast, xmlrc_resource_file());
  strcat(flast, ".last");
  cc = rename(xmlrc_resource_file(), flast);
  if (cc != 0)
    ERR("rename %s, %s, cc:%d, errno:%d",
         xmlrc_resource_file(), flast, cc, errno);

  cc = xmlSaveFormatFile(xmlrc_resource_file(), doc, 1);
  if (cc <= 0)
    ERR("xmlSaveFormatFile, file:%s, cc:%d", xmlrc_resource_file(), cc);

}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void xmlrc_set_resource_file(char *config_file)
{
  g_assert(g_xmlrc_filerc[0] == '\0'); // set filerc ONE time only
  if (config_file != NULL)
  {
    if (strlen(config_file) >= FILENAME_MAX)
      ERR("config file name:%d to big", strlen(config_file));
    strcat(g_xmlrc_filerc, config_file);
  }
  else
  {
    char *home;
    home = getenv("HOME");
    if (home == NULL)
      ERR("%s", "environement variable $HOME not set");
    strcpy(g_xmlrc_filerc, home);
    strcat(g_xmlrc_filerc, "/.apwalrc.xml");
  }
}
// ----------------------------------------------------------------------------
static char* xmlrc_resource_file(void)
{
  g_assert(g_xmlrc_filerc[0] != '\0'); // filerc have to be set
  return g_xmlrc_filerc;
}
// ----------------------------------------------------------------------------
gboolean xmlrc_resource_file_exist(void)
{
  char *file;
  gboolean is_exist;
  file = xmlrc_resource_file();
  is_exist = g_file_test(file, G_FILE_TEST_EXISTS);
  return is_exist;
}
// ----------------------------------------------------------------------------
void xmlrc_resource_file_create(void)
{
  int len, fd, cc;
  fd = creat(xmlrc_resource_file(), 0755);
  if (fd < 0)
    ERR("creation of the resource file failed. open cc:%d, errno:%d, file:%s",
        fd, errno, xmlrc_resource_file());
  len = strlen(XMLRC_DEFAULT);
  cc = write(fd, XMLRC_DEFAULT, len);
  if (cc != len)
    ERR("creation of the resource file failed. write cc:%d, errno:%d, file:%s",
        cc, errno, xmlrc_resource_file());
  close(fd);
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
