/*
   Search text engine.
   Glob-style pattern matching

   Copyright (C) 2009-2025
   Free Software Foundation, Inc.

   Written by:
   Slava Zanko <slavazanko@gmail.com>, 2009.

   This file is part of the Midnight Commander.

   The Midnight Commander 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.

   The Midnight Commander 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, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>

#include "lib/global.h"
#include "lib/strutil.h"
#include "lib/search.h"

#include "internal.h"

/*** global variables ****************************************************************************/

/*** file scope macro definitions ****************************************************************/

/*** file scope type declarations ****************************************************************/

/*** forward declarations (file scope functions) *************************************************/

/*** file scope variables ************************************************************************/

/* --------------------------------------------------------------------------------------------- */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */

static GString *
mc_search__glob_translate_to_regex (const GString *astr)
{
    GString *buff;
    gsize loop;
    gboolean inside_group = FALSE;

    buff = g_string_sized_new (32);

    for (loop = 0; loop < astr->len; loop++)
    {
        const char *str = astr->str;
        gboolean not_escaped;

        not_escaped = !str_is_char_escaped (str, str + loop);

        switch (str[loop])
        {
        case '*':
            if (not_escaped)
            {
                g_string_append (buff, inside_group ? ".*" : "(.*)");
                continue;
            }
            break;
        case '?':
            if (not_escaped)
            {
                g_string_append (buff, inside_group ? "." : "(.)");
                continue;
            }
            break;
        case ',':
            if (not_escaped)
            {
                g_string_append_c (buff, inside_group ? '|' : ',');
                continue;
            }
            break;
        case '{':
            if (not_escaped)
            {
                g_string_append_c (buff, '(');
                inside_group = TRUE;
                continue;
            }
            break;
        case '}':
            if (not_escaped)
            {
                g_string_append_c (buff, ')');
                inside_group = FALSE;
                continue;
            }
            break;
        case '+':
        case '.':
        case '$':
        case '(':
        case ')':
        case '^':
            g_string_append_c (buff, '\\');
            break;
        default:
            break;
        }
        g_string_append_c (buff, str[loop]);
    }
    return buff;
}

/* --------------------------------------------------------------------------------------------- */

static GString *
mc_search__translate_replace_glob_to_regex (const char *str)
{
    GString *buff;
    char cnt = '0';
    gboolean escaped_mode = FALSE;

    buff = g_string_sized_new (32);

    while (*str != '\0')
    {
        char c = *str++;

        switch (c)
        {
        case '\\':
            if (!escaped_mode)
            {
                escaped_mode = TRUE;
                g_string_append_c (buff, '\\');
                continue;
            }
            break;
        case '*':
        case '?':
            if (!escaped_mode)
            {
                g_string_append_c (buff, '\\');
                c = ++cnt;
            }
            break;
        case '&':
            if (!escaped_mode)
                g_string_append_c (buff, '\\');
            break;
        default:
            break;
        }
        g_string_append_c (buff, c);
        escaped_mode = FALSE;
    }
    return buff;
}

/*** public functions ****************************************************************************/

void
mc_search__cond_struct_new_init_glob (const char *charset, mc_search_t *lc_mc_search,
                                      mc_search_cond_t *mc_search_cond)
{
    GString *tmp;

    tmp = mc_search__glob_translate_to_regex (mc_search_cond->str);
    g_string_free (mc_search_cond->str, TRUE);

    if (lc_mc_search->is_entire_line)
    {
        g_string_prepend_c (tmp, '^');
        g_string_append_c (tmp, '$');
    }
    mc_search_cond->str = tmp;

    mc_search__cond_struct_new_init_regex (charset, lc_mc_search, mc_search_cond);
}

/* --------------------------------------------------------------------------------------------- */

gboolean
mc_search__run_glob (mc_search_t *lc_mc_search, const void *user_data,
                     off_t start_search, off_t end_search, gsize *found_len)
{
    return mc_search__run_regex (lc_mc_search, user_data, start_search, end_search, found_len);
}

/* --------------------------------------------------------------------------------------------- */

GString *
mc_search_glob_prepare_replace_str (mc_search_t *lc_mc_search, GString *replace_str)
{
    GString *repl, *res;

    repl = mc_search__translate_replace_glob_to_regex (replace_str->str);
    res = mc_search_regex_prepare_replace_str (lc_mc_search, repl);
    g_string_free (repl, TRUE);

    return res;
}

/* --------------------------------------------------------------------------------------------- */
