/* Chrysalide - Outil d'analyse de fichiers binaires
 * import.c - localisation de fichiers de définitions externes
 *
 * Copyright (C) 2023 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide 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 Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "import.h"


#include <libgen.h>
#include <malloc.h>
#include <string.h>


#include <common/environment.h>
#include <core/logs.h>



/* Charge un type Kaitai à partir d'une définition voisine. */
static GKaitaiType *import_relative_kaitai_definition(const char *, const char *);

/* Charge un type Kaitai depuis un emplacement de $KSPATH. */
static GKaitaiType *import_kaitai_definition_from_env(const char *);

/* Charge un interpréteur pour une définition voisine. */
static GKaitaiStruct *load_relative_kaitai_definition(const char *, const char *);

/* Charge un interpréteur depuis un emplacement de $KSPATH. */
static GKaitaiStruct *load_kaitai_definition_from_env(const char *);



/******************************************************************************
*                                                                             *
*  Paramètres  : target    = désignation de la définition à retrouver.        *
*                reference = éventuel fichier pour les positions relatives.   *
*                                                                             *
*  Description : Charge un type Kaitai à partir d'une définition voisine.     *
*                                                                             *
*  Retour      : Type valide en place ou NULL en cas d'échec.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GKaitaiType *import_relative_kaitai_definition(const char *target, const char *reference)
{
    GKaitaiType *result;                    /* Structure chargée à renvoyer*/
    char *tmp;                              /* Zone de travail temporaire  */
    char *base;                             /* Base de recherche           */
    char *filename;                         /* Nom de fichier à tester     */
    int ret;                                /* Bilan d'une construction    */

    result = NULL;

    tmp = strdup(reference);
    base = dirname(tmp);

    ret = asprintf(&filename, "%s%c%s.ksy", base, G_DIR_SEPARATOR, target);
    if (ret == -1) goto build_error;

    result = g_kaitai_type_new_as_import(target, filename);

    free(filename);

 build_error:

    free(tmp);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : target = désignation de la définition à retrouver.           *
*                                                                             *
*  Description : Charge un type Kaitai depuis un emplacement de $KSPATH.      *
*                                                                             *
*  Retour      : Type valide en place ou NULL en cas d'échec.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GKaitaiType *import_kaitai_definition_from_env(const char *target)
{
    GKaitaiType *result;                    /* Structure chargée à renvoyer*/
    char *paths;                            /* Emplacements de greffons    */
    char *save;                             /* Sauvegarde pour ré-entrance */
    char *path;                             /* Chemin à fouiller           */
    char *filename;                         /* Nom de fichier à tester     */
    int ret;                                /* Bilan d'une construction    */

    result = NULL;

    paths = get_env_var("KSPATH");

    save = NULL;   /* gcc... */

    for (path = strtok_r(paths, ":", &save);
         path != NULL;
         path = strtok_r(NULL, ":", &save))
    {
        ret = asprintf(&filename, "%s%c%s.ksy", path, G_DIR_SEPARATOR, target);
        if (ret == -1)
        {
            LOG_ERROR_N("asprintf");
            continue;
        }

        result = g_kaitai_type_new_as_import(target, filename);

        if (result != NULL)
            log_variadic_message(LMT_PROCESS, _("Found a required Kaitai definition to import: %s"), filename);

        free(filename);

    }

    free(paths);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : target    = désignation de la définition à retrouver.        *
*                reference = éventuel fichier pour les positions relatives.   *
*                                                                             *
*  Description : Met en place un type Kaitai pour une définition désignée.    *
*                                                                             *
*  Retour      : Type valide en place ou NULL en cas d'échec.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GKaitaiType *import_kaitai_definition(const char *target, const char *reference)
{
    GKaitaiType *result;                    /* Structure chargée à renvoyer*/

    result = NULL;

    if (reference != NULL)
        result = import_relative_kaitai_definition(target, reference);

    if (result == NULL)
        result = import_kaitai_definition_from_env(target);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : target    = désignation de la définition à retrouver.        *
*                reference = éventuel fichier pour les positions relatives.   *
*                                                                             *
*  Description : Charge un interpréteur pour une définition voisine.          *
*                                                                             *
*  Retour      : Interprétateur valide en place ou NULL en cas d'échec.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GKaitaiStruct *load_relative_kaitai_definition(const char *target, const char *reference)
{
    GKaitaiStruct *result;                  /* Structure chargée à renvoyer*/
    char *tmp;                              /* Zone de travail temporaire  */
    char *base;                             /* Base de recherche           */
    char *filename;                         /* Nom de fichier à tester     */
    int ret;                                /* Bilan d'une construction    */

    result = NULL;

    tmp = strdup(reference);
    base = dirname(tmp);

    ret = asprintf(&filename, "%s%c%s.ksy", base, G_DIR_SEPARATOR, target);
    if (ret == -1) goto build_error;

    result = g_kaitai_structure_new_from_file(filename);

    free(filename);

 build_error:

    free(tmp);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : target = désignation de la définition à retrouver.           *
*                                                                             *
*  Description : Charge un interpréteur depuis un emplacement de $KSPATH.     *
*                                                                             *
*  Retour      : Interprétateur valide en place ou NULL en cas d'échec.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GKaitaiStruct *load_kaitai_definition_from_env(const char *target)
{
    GKaitaiStruct *result;                  /* Structure chargée à renvoyer*/
    char *paths;                            /* Emplacements de greffons    */
    char *save;                             /* Sauvegarde pour ré-entrance */
    char *path;                             /* Chemin à fouiller           */
    char *filename;                         /* Nom de fichier à tester     */
    int ret;                                /* Bilan d'une construction    */

    result = NULL;

    paths = get_env_var("KSPATH");

    save = NULL;   /* gcc... */

    for (path = strtok_r(paths, ":", &save);
         path != NULL;
         path = strtok_r(NULL, ":", &save))
    {
        ret = asprintf(&filename, "%s%c%s.ksy", path, G_DIR_SEPARATOR, target);
        if (ret == -1)
        {
            LOG_ERROR_N("asprintf");
            continue;
        }

        result = g_kaitai_structure_new_from_file(filename);

        if (result != NULL)
            log_variadic_message(LMT_PROCESS, _("Found a required Kaitai definition: %s"), filename);

        free(filename);

    }

    free(paths);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : target    = désignation de la définition à retrouver.        *
*                reference = éventuel fichier pour les positions relatives.   *
*                                                                             *
*  Description : Met en place un interpréteur pour une définition désignée.   *
*                                                                             *
*  Retour      : Interprétateur valide en place ou NULL en cas d'échec.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GKaitaiStruct *load_kaitai_definition(const char *target, const char *reference)
{
    GKaitaiStruct *result;                  /* Structure chargée à renvoyer*/

    result = NULL;

    if (reference != NULL)
        result = load_relative_kaitai_definition(target, reference);

    if (result == NULL)
        result = load_kaitai_definition_from_env(target);

    return result;

}