#include "KMBuilderUtils.h"

#include <iostream>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <gio/gunixinputstream.h>
#include <ostree.h>
#include <boost/process.hpp>

#include "KMBuilderGlobal.h"
#include "common/KMLogger.h"

#define DEBUGEDIT "/usr/local/libexec/kaiming-builder-debugedit"

using namespace std;
namespace bp = boost::process;

#if !defined(__i386__) && !defined(__x86_64__) && !defined(__aarch64__) && !defined(__arm__)
static string get_kernel_arch()
{
    static struct utsname buf;
    static char *arch = nullptr;

    if (arch != nullptr)
        return arch;

    if (uname(&buf))
    {
        arch = "unknown";
        return arch;
    }

    arch = buf.machine;

    /* Override for some arches */
    string mstr = buf.machine;
    if (mstr.length() == 4 && mstr.at(0) == 'i' && mstr.at(2) == '8' && mstr.at(3) == '6')
    {
        arch = "i386";
    }
    else if (mstr == "mips")
    {
#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
        arch = "mipsel";
#  endif
    }
    else if (mstr == "mips64")
    {
#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
        arch = "mips64el";
#  endif
    }

    return arch;
}
#endif

KMBuilderUtils::KMBuilderUtils() { }

KMBuilderUtils::~KMBuilderUtils() { }

list<string> KMBuilderUtils::split(string str, char c)
{
    list<string> retList;

    int indexStart = 0;
    int indexEnd = -1;
    do
    {
        indexEnd = str.find(c, indexStart);

        if (indexEnd < 0)
        {
            retList.push_back(str.substr(indexStart, str.length() - indexStart));
            break;
        }

        // 针对  "20.03;askkd;qwecc;"
        if (indexEnd == str.length() - 1)
        {
            retList.push_back(str.substr(indexStart, str.length() - indexStart - 1));
            break;
        }

        retList.push_back(str.substr(indexStart, indexEnd - indexStart));
        indexStart = indexEnd + 1;
    } while (true);

    return retList;
}

const string KMBuilderUtils::getArch()
{
#if defined(__i386__)
    return "i386";
#elif defined(__x86_64__)
    return "x86_64";
#elif defined(__aarch64__)
    return "arm64";
#elif defined(__arm__)
#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
    return "arm";
#  else
    return "armeb";
#  endif
#else
    return get_kernel_arch();
#endif
}

bool KMBuilderUtils::ensureDir(fs::path dir)
{
    if (fs::exists(dir))
        return true;

    return KMBuilderUtils::mkpath(dir.string());
}

const string KMBuilderUtils::getCommandResoult(const string &exe, const list<string> &args)
{
    string result;

    string cmd = exe;

    for (auto arg : args)
        cmd.append(" ").append(arg);

    FILE *pipe = popen(cmd.c_str(), "r");
    if (!pipe)
        return string();

    size_t ret = 0;
    char buf[1025] = { 0 };
    while ((ret = fread(buf, sizeof(char), 1024, pipe)) == 1024)
    {
        result.append(buf);
    }
    if (feof(pipe) != 0)
        return string();

    result.append(buf);

    return result;
}

bool KMBuilderUtils::zeroMtime(int fd, const string &path)
{
    struct stat stbuf;
    if (TEMP_FAILURE_RETRY(fstatat(fd, path.c_str(), &stbuf, AT_SYMLINK_NOFOLLOW)) != 0)
        return false;

    const struct timespec times[2] = { { 0, UTIME_OMIT }, { OSTREE_TIMESTAMP } };

    if (S_ISDIR(stbuf.st_mode) && stbuf.st_mtime != OSTREE_TIMESTAMP)
    {
        for (auto const &direntry : fs::directory_iterator{ path })
        {
            fs::path p = direntry.path();
            if (fs::is_directory(p))
            {
                if (!zeroMtime(AT_FDCWD, p.string()))
                    return false;
            }
            else if (fs::is_regular_file(p))
            {
                if (TEMP_FAILURE_RETRY(utimensat(AT_FDCWD, path.c_str(), times, AT_SYMLINK_NOFOLLOW)) != 0)
                    return false;
            }
        }
    }

    if (stbuf.st_mtime != OSTREE_TIMESTAMP)
    {
        if (TEMP_FAILURE_RETRY(utimensat(fd, path.c_str(), times, AT_SYMLINK_NOFOLLOW)) != 0)
            return false;
    }

    return true;
}

/**
 * @brief : 执行外部程序
 * @param : [in] exec, 外部程序名称，不包含路径
 * @param : [in] args, 执行外部程序需要传入的参数列表
 * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
 * @return: true - 成功执行；false - 出现错误
*/
bool KMBuilderUtils::spawn(const std::string &exec, const std::vector<std::string> &args, std::string &result)
{
    std::string cmdline = exec;
    for (auto &arg : args)
    {
        cmdline += " ";
        if (KMBuilderUtils::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }
    }
    KMDebug(cmdline);
    
    bp::ipstream is;
    bp::child c(bp::search_path(exec), bp::args = args, (bp::std_out & bp::std_err) > is);
    std::string line;
    bool first = true;
    while (is && std::getline(is, line))
    {
        if (first)
        {
            first = false;
        }
        else
        {
            result += "\n";
        }
        result += line;
    }
    c.wait();

    if (c.exit_code() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief : 执行外部程序
 * @param : [in] exec, 外部程序
 * @param : [in] args, 执行外部程序需要传入的参数列表
 * @return: true - 成功执行；false - 出现错误
*/
bool KMBuilderUtils::spawn(const std::string &exec, const std::vector<std::string> &args)
{
    std::string cmdline = exec;
    for (auto &arg : args)
    {
        cmdline += " ";
        if (KMBuilderUtils::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }
    }
    KMDebug(cmdline);
    
    bp::ipstream is;
    bp::child c(bp::search_path(exec), bp::args = args, (bp::std_out & bp::std_err) > is);
    std::string line;
    while (is && std::getline(is, line))
    {
       std::cout << line << std::endl;
    }
    c.wait();

    if (c.exit_code() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief : 执行外部程序
 * @param : [in] exec, 外部程序名称，不包含路径
 * @param : [in] workingDirectory, 执行外部程序工作目录
 * @param : [in] args, 执行外部程序需要传入的参数列表
 * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
 * @return: true - 成功执行；false - 出现错误
*/
bool KMBuilderUtils::spawn(const std::string &exec, const std::string &workingDirectory, const std::vector<std::string> &args, std::string &result)
{
    KMDebug("cd " + workingDirectory);
    std::string cmdline = exec;
    for (auto &arg : args)
    {
        cmdline += " ";
        if (KMBuilderUtils::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }
    }
    KMDebug(cmdline);

    bp::ipstream is;
    bp::child c(bp::search_path(exec), bp::start_dir(workingDirectory), bp::args = args, (bp::std_out & bp::std_err) > is);
    std::string line;
    bool first = true;
    while (is && std::getline(is, line))
    {
        if (first)
        {
            first = false;
        }
        else
        {
            result += "\n";
        }
        result += line;
    }
    c.wait();

    if (c.exit_code() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}


/**
 * @brief : 根据分隔符拆分字符串
 * @param : [in] str，源字符串
 * @param : [in] split，分隔符
 * @return: std::vector<std::string>，拆分后的字符串数组
 */
std::vector<std::string> KMBuilderUtils::splitString(const std::string &str, const std::string &split)
{
    std::vector<std::string> values;
    if (str.empty())
        return values;

    int index_start = 0;
    int index_end = -1;
    do
    {
        index_end = str.find(split, index_start);

        if (index_end == std::string::npos)
        {
            values.push_back(str.substr(index_start, str.length() - index_start));
            break;
        }
        else
        {
            values.push_back(str.substr(index_start, index_end - index_start));
        }

        index_start = index_end + split.length();
    } while (index_start < str.length() - 1);

    return values;
}

/**
 * @brief : 将字符串数组合并成一个字符串，各个元素之间用split分割
 * @param : [in] vector，待合并的字符串数组
 * @param : [in] split，分隔符
 * @return: 合并后的长字符串
 */
std::string KMBuilderUtils::join(const std::vector<std::string>& vector, const std::string &split)
{
    std::string value;
    for (auto &e : vector)
    {
        if (!value.empty())
        {
            value += split;
        }
        value += e;
    }

    return value;
}

bool KMBuilderUtils::argumentNeedsQuoting(const char *arg)
{
    if (*arg == '\0')
        return true;

    while (*arg != 0)
    {
        char c = *arg;
        if (!std::isalnum(c) && !(c == '-' || c == '/' || c == '~' || c == ':' || c == '.' || c == '_' || c == '=' || c == '@'))
            return true;
        arg++;
    }
    return false;
}

/**
 * isEmptyDirectory:
 * @brief : 判断路径是否空目录，使用前先判断是否是目录
 * @param : [in] path，路径
 * @return: true-空目录或不存在; false-不是空目录或不是目录
 */
bool KMBuilderUtils::isEmptyDirectory(const std::string &path)
{
    fs::path p(path);
    if (!fs::exists(p))
        return true;

    if (!fs::is_directory(p))
        return false;

    bool ret = true;
    for (auto const &dirEntry : fs::directory_iterator{ p })
    {
        std::string filename = dirEntry.path().filename();
        if (filename != "." && filename != "..")
        {
            ret = false;
            break;
        }
    }

    return ret;
}

/**
 * removeEmptyDirectory:
 * @brief : 递归删除空子目录
 * @param : [in] path，路径
 * @return: true-操作成功; false-操作失败
 */
bool KMBuilderUtils::removeEmptyDirectory(const std::string &path, std::error_code &ec)
{
    fs::path p(path);
    if (!fs::exists(p))
    {
        return true;
    }

    if (!fs::is_directory(p))
    {
        return false;
    }
    
    for (auto const &dirEntry : fs::directory_iterator{ p })
    {
        fs::path subdir = dirEntry.path();
        std::string filename = subdir.filename().string();

        if (filename == "." || filename == "..")
        {
            continue;
        }

        if (!fs::is_directory(subdir))
        {
            continue;
        }

        if (KMBuilderUtils::isEmptyDirectory(subdir.string()))
        {
            bool result = fs::remove(subdir, ec);
            if (!result)
            {
                return false;
            }
        }
        else
        {
            bool result = KMBuilderUtils::removeEmptyDirectory(subdir.string(), ec);
            if (!result)
            {
                return false;
            }

            if (KMBuilderUtils::isEmptyDirectory(subdir.string()))
            {
                result = fs::remove(subdir, ec);
                if (!result)
                {
                    return false;
                }
            }
        }
    }

    return true;
}

/**
 * @brief : 递归删除目录或文件
 * @param : [in] path, 待目录或文件
 * @param : [out] std::error_code，保存错误信息
 * @return : true-成功；false-失败
 */
bool KMBuilderUtils::removeAll(const std::string &path, std::error_code &ec)
{
    // 移除文件或目录的只读属性
    fs::permissions(path, fs::perms::owner_all | fs::perms::group_read | fs::perms::group_exec | fs::perms::others_read | fs::perms::others_exec, ec);

    bool result = fs::remove_all(path, ec);
    if (!result && 0 == ec.value())
        return true;
    return result;
}

/**
 * @brief : 递归删除目录或文件
 * @param : [in] path, 待目录或文件
 * @return : true-成功；false-失败
 */
bool KMBuilderUtils::removeAll(const std::string &path)
{
    std::error_code ec;
    bool result = KMBuilderUtils::removeAll(path, ec);
    if (!result)
    {
        KMWarn(ec.message());
    }

    return result;
}

bool KMBuilderUtils::rmRfAll(const std::string &path)
{
    std::vector<std::string> args;
    args.push_back("-rf");
    args.push_back(path);

    std::string result;
    if (!KMBuilderUtils::spawn("rm", args, result))
    {
        KMWarn(result);
        return false;
    }
    
    return true;
}

/**
 * @brief : 判断字符串前缀
 * @param : [in] src，原始字符串
 * @param : [in] prefix，字符串前缀
 * @return : true-字符串src是以prefix为前缀；false-字符串src不是以prefix为前缀
 */
bool KMBuilderUtils::startsWith(const std::string &src, const std::string &prefix)
{
    return src.find(prefix) == 0;
}

/**
 * @brief : 判断字符串后缀
 * @param : [in] src，原始字符串
 * @param : [in] sufix，字符串后缀
 * @return : true-字符串src是以sufix为后缀；false-字符串src不是以sufix为后缀
 */
bool KMBuilderUtils::endsWith(const std::string &src, const std::string &sufix)
{
    size_t pos = src.rfind(sufix);
    if (pos == std::string::npos)
        return false;

    size_t sufixLen = sufix.length();
    size_t srcLen = src.length();

    return pos + sufix.length() == src.length();
}

/**
 * @brief : 组装文件名
 * @param : [in] path, 前缀路径
 * @param : [in] name, 后续文件或目录名
 * @return: 组装后的文件路径
 */
std::string KMBuilderUtils::buildFilename(const std::string &path, const std::string &name)
{
    if (path.empty())
        return name;

    if (name.empty())
        return path;

    std::string filename = path + "/" + name;
    KMBuilderUtils::replace(filename, "//", "/");
    KMBuilderUtils::replace(filename, "/./", "/");

    return filename;
}

std::string KMBuilderUtils::buildFilename(const std::string &path, const std::string &name1, const std::string &name2)
{
    std::string filename = path + "/" + name1 + "/" + name2;
    KMBuilderUtils::replace(filename, "//", "/");

    return filename;
}

/**
 * @brief : 将字符串str中的子串from替换为to
 * @param : [in | out] str, 原字符串
 * @param : [in] from, 需要替换的旧字符子串
 * @param : [in] to, 替换后的新子串
 * @return: 替换后的字符串引用，同str
 */
std::string &KMBuilderUtils::replace(std::string &str, const std::string &from, const std::string &to)
{
    // 替换所有出现的from为to
    size_t startPos = 0;
    while ((startPos = str.find(from, startPos)) != std::string::npos)
    {
        str.replace(startPos, from.length(), to);
        startPos += to.length(); // 防止无限循环
    }

    return str;
}


/**
 * @brief : 递归创建目录
 * @param : [in] path, 目录
 * @param : [out] std::error_code，保存错误信息
 * @return : true-存在；false-不存在
 */
bool KMBuilderUtils::mkpath(const std::string &path, std::error_code &ec)
{
    bool result = fs::create_directories(path, ec);
    if (!result && 0 == ec.value())
        return true;
    return result;
}

/**
 * @brief : 递归创建目录，如果目录已经存在则不报告错误
 * @param : [in] path, 目录
 * @return : true-存在；false-不存在
 */
bool KMBuilderUtils::mkpath(const std::string &path)
{
    std::error_code ec;
    return KMBuilderUtils::mkpath(path, ec);
}

/**
 * @brief : 递归创建目录，如果目录已经存在则不报告错误
 * @param : [in] path, 目录
 * @param : [in] mode，路径权限，可八进制写法，如：0755
 * @return : true-存在；false-不存在
 */
bool KMBuilderUtils::mkpath(const std::string &path, unsigned int mode)
{
    std::error_code ec;
    if (KMBuilderUtils::mkpath(path, ec))
    {
        fs::permissions(path, (fs::perms)mode);
        return true;
    }

    return false;
}

/**
 * @brief : 在字符串数组v中查找是否包含字符串e
 * @param : [in] v, 字符串数组
 * @param : [in] e, 字符串
 * @return: true-包含；false-不包含
 */
bool KMBuilderUtils::contains(const std::vector<string> &v, const std::string &e)
{
    for (auto &element : v)
    {
        if (e == element)
        {
            return true;
        }
    }

    return false;
}

bool KMBuilderUtils::contains(const std::string &src, const std::string &sub)
{
    return std::string::npos != src.find(sub);
}

/**
 * @brief : 下载url数据到文件dest
 * @param : [in] url，支持HTTP、HTTPS、FTP、FTPS协议
 * @param : [in] 目标文件名，默认则为url的basename
 * @return: true-下载成功；false-下载失败
 * @note  : 使用wget命令实现，优势：可利用其断点续传和代理等
 */ 
bool KMBuilderUtils::downloadUrl(const std::string &url, const std::string &dest)
{
    std::vector<std::string> args;
    args.push_back("-c");
    if (!dest.empty())
    {
        args.push_back("-O");
        args.push_back(dest);

        std::string dir = fs::path(dest).parent_path().string();
        KMBuilderUtils::ensureDir(dir);
    }
    args.push_back("-T");
    args.push_back("3");
    args.push_back("--no-check-certificate");
    args.push_back(url);

    bool ret = KMBuilderUtils::spawn("wget", args);

    return ret;
}

/**
 * @brief : 启动进程
 * @param dir : 工作目录
 * @param flags : 进程标志
 * @param args : 进程参数
 * @return : true-成功; false-失败
 */
bool KMBuilderUtils::spawn(const string &dir, int flags, const vector<string> &args)
{
    if (args.empty())
        return false;

    g_autoptr(GSubprocessLauncher) launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE);

    // g_subprocess_launcher_setenv(launcher, "LANGUAGE", "C", TRUE);
    g_subprocess_launcher_set_flags(launcher, static_cast<GSubprocessFlags>(flags));

    if (!dir.empty())
        g_subprocess_launcher_set_cwd(launcher, dir.c_str());

    vector<char *> argv;
    for (auto &arg : args)
        argv.emplace_back(const_cast<char *>(arg.c_str()));
    argv.emplace_back(nullptr);

    std::string cmdline;
    for (auto &arg : args)
    {
        if (KMBuilderUtils::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }

        cmdline += " ";
    }
    KMDebug(cmdline);

    g_autoptr(GSubprocess) subp = g_subprocess_launcher_spawnv(launcher, (char **)argv.data(), nullptr);
    if (subp == nullptr)
        return false;

    g_subprocess_wait(subp, nullptr, nullptr);

    if (g_subprocess_get_if_exited(subp))
    {
        if (g_subprocess_get_exit_status(subp))
            return false;
    }
    else
        return false;

    return true;
}

string KMBuilderUtils::argSplice(string prefix, string content)
{
    std::ostringstream streamBuffer;
    streamBuffer << prefix << content;
    string strBuffer = streamBuffer.str();
    return strBuffer;
}

string KMBuilderUtils::argSplice(string prefix, string content, string suffix)
{
    std::ostringstream streamBuffer;
    streamBuffer << prefix << content << suffix;
    string strBuffer = streamBuffer.str();
    return strBuffer;
}

string KMBuilderUtils::argSplice(string prefix, int content)
{
    std::ostringstream streamBuffer;
    streamBuffer << prefix << content;
    string strBuffer = streamBuffer.str();
    return strBuffer;
}

string KMBuilderUtils::getChildPath(const string &parentPath, const string &childName)
{
    // 如果 parentPath 不以斜杠结尾，则添加一个斜杠
    string path = parentPath;
    if (!path.empty() && path.back() != '/' && path.back() != '\\')
    {
        path += '/';
    }
    // 将子项名称添加到路径末尾
    path += childName;
    return path;
}

bool KMBuilderUtils::pathExists(const std::string &path)
{
    return (access(path.c_str(), F_OK) != -1);
}

string KMBuilderUtils::replaceIdPrefix(const string &origId)
{
    string id = origId;
    // 使用 std::replace_if 和 lambda 表达式替换 '-' 为 '_' 直到遇到 '/'
    auto it = find(id.begin(), id.end(), '/');
    if (it != id.end())
    {
        replace_if(
          id.begin(),
          it,
          [](char c)
          {
              return c == '-';
          },
          '_');
    }
    else
    {
        replace_if(
          id.begin(),
          id.end(),
          [](char c)
          {
              return c == '-';
          },
          '_');
    }
    return id;
}

bool KMBuilderUtils::command(string appDir, vector<string> envVars, vector<string> extraArgs, string commandLine)
{
    vector<string> args;

    args.push_back("kaiming");
    args.push_back("build");
    args.push_back("--die-with-parent");
    args.push_back("--nofilesystem=host:reset");

    if (extraArgs.size())
    {
        for (auto extraArg : extraArgs)
        {
            args.push_back(extraArg);
        }
    }

    if (envVars.size())
    {
        for (auto envVar : envVars)
        {
            args.push_back(KMBuilderUtils::argSplice("--env=", envVar));
        }
    }

    args.push_back(appDir);
    args.push_back("/bin/sh");
    args.push_back("-c");
    args.push_back(commandLine);

    if (KMBuilderUtils::isInSanbox())
    {
        return KMBuilderUtils::hostSpawn("", 0, args);
    }
    return KMBuilderUtils::spawn("", 0, args);
}

/**
 * @brief : 创建目录软连接
 * @param : target 目标目录
 * @param : link 连接符号
 * @param : ec 错误信息
 * @return: true-创建成功；false-创建失败
 */
bool KMBuilderUtils::createDirectorySymlink(const std::string &target, const std::string &link, std::error_code &ec)
{
    fs::create_directory_symlink(target, link, ec);
    if (0 == ec.value())
        return true;
    else
        return false;
}

/**
 * @brief : 是否在沙箱
 * @return: true-是；false-不是
 */
bool KMBuilderUtils::isInSanbox()
{
    string runtimeDir = g_get_user_runtime_dir();
    fs::path info(runtimeDir + "/kaiming-info");

    if (fs::exists(info) && fs::is_regular_file(info))
        return true;

    return false;
}

/**
 * @brief : 沙箱启动进程
 * @param dir : 工作目录
 * @param flags : 进程标志
 * @param args : 进程参数
 * @return : true-成功; false-失败
 */
bool KMBuilderUtils::hostSpawn(const string &dir, int flags, const vector<string> &args)
{
    // TODO: 后续补充沙箱中启动进程
    //  int cmd_flags = 3;
    //  guint32 client_pid;
    //  GVariantBuilder *fd_builder = g_variant_builder_new(G_VARIANT_TYPE("a{uh}"));
    //  GVariantBuilder *env_builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}"));

    // string cwd = dir;
    // if (cwd.empty())
    // {
    //   g_autofree char *pCurDir = g_get_current_dir();
    //   cwd.assign(pCurDir);
    // }

    // g_autoptr(GDBusConnection) pDbusConnection = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
    // if (pDbusConnection == nullptr)
    //   return false;

    return true;
}

/**
 * @brief : 是否有符号表
 * @param elf : elf文件
 * @return : true-是; false-否
 */
bool KMBuilderUtils::hasSymbol(Elf *elf)
{
    Elf_Scn *scn = nullptr;
    GElf_Shdr shdr;

    while ((scn = elf_nextscn(elf, scn)) != NULL)
    {
        if (gelf_getshdr(scn, &shdr) == NULL)
            continue;

        if (shdr.sh_type != SHT_SYMTAB)
            continue;

        return true;
    }

    return false;
}

/**
 * @brief : 是否是ELF文件
 * @param path : 文件路径
 * @param isShared : 是否是共享库
 * @param isStripped : 是否是已剥离的共享库
 * @return : true-是; false-否
 */
bool KMBuilderUtils::isElfFile(const string &path, bool &isShared, bool &isStripped)
{
    fs::path filepath(path);
    if (!fs::is_regular_file(filepath))
        return false;

    string ext = filepath.extension().string();
    const vector<string> ignoreExts = { ".zip", "tar.gz", "tar.bz2", "tar.xz", ".h", ".txt", ".mo", "m4" };
    for (auto &ignore : ignoreExts)
    {
        if (ext == ignore)
            return false;
    }

    int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
    if (fd < 0)
        return false;

    Elf *elf;
    GElf_Ehdr ehdr;
    bool res = false;

    if (elf_version(EV_CURRENT) == EV_NONE)
        return false;

    elf = elf_begin(fd, ELF_C_READ, nullptr);
    if (elf == nullptr)
        return FALSE;

    if (elf_kind(elf) == ELF_K_ELF && gelf_getehdr(elf, &ehdr))
    {
        isShared = ehdr.e_type == ET_DYN;
        isStripped = !hasSymbol(elf);
        res = true;
    }

    elf_end(elf);

    close(fd);

    return res;
}

/**
 * @brief : 获取目录下所有文件
 * @param : dir 查找的目标目录
 * @param : files 所有的文件
 */
void KMBuilderUtils::getFilesInDir(const std::string &dir, vector<string> &files)
{
    for (auto const &dirEntry : fs::directory_iterator{ dir })
    {
        fs::path p = dirEntry.path();
        if (fs::is_symlink(p))
            continue;
        else if (fs::is_regular_file(p))
            files.emplace_back(p.string());
        else if (fs::is_directory(p))
            getFilesInDir(p.string(), files);
    }
}

/**
 * @brief : 获取debuginfo文件引用
 * @param : filename 文件名
 * @param : fileRefs 文件引用
 * @return : true-成功; false-失败
 */
bool KMBuilderUtils::getDebuginfoFileRref(const string &filename, vector<string> &fileRefs)
{
    string tmppath = string(g_get_tmp_dir()) + "/kaiming-debugedit-list.XXXXXX";
    int tmpfd = g_mkstemp((gchar *)tmppath.c_str());
    if (tmpfd == -1)
        return false;

    string debugEdit;
    char *p = getenv("KAIMING_BUILDER_DEBUGEDIT");
    if (p == nullptr)
        debugEdit = DEBUGEDIT;
    else
        debugEdit = p;

    vector<string> args = { debugEdit, string("-l"), tmppath, filename };

    if (!KMBuilderUtils::spawn("", 0, args))
        return false;

    g_autoptr(GInputStream) inputstream = g_unix_input_stream_new(tmpfd, FALSE);
    g_autoptr(GDataInputStream) datastream = g_data_input_stream_new(inputstream);

    while (true)
    {
        g_autoptr(GError) error = nullptr;
        g_autofree char *file = g_data_input_stream_read_upto(datastream, "\0", 1, nullptr, nullptr, &error);
        if (file == nullptr)
        {
            if (error == nullptr)
                break;

            fileRefs.clear();
            return true;
        }

        g_data_input_stream_read_byte(datastream, nullptr, nullptr);
        if (*file == '\0')
            continue;

        fileRefs.emplace_back(string(file));
    }

    return true;
}

bool KMBuilderUtils::breakHardlink(const std::filesystem::path &filePath)
{
    // 检查文件是否存在
    if (!std::filesystem::exists(filePath))
    {
        std::cerr << "File does not exist: %s." << filePath.string() << std::endl;
        return false;
    }

    // 获取文件父目录
    const auto parentPath = filePath.parent_path();

    // 创建临时文件名
    std::filesystem::path tmpPath = parentPath / ".breaklinkXXXXXX";

    try
    {
        // 复制文件
        std::filesystem::copy(filePath, tmpPath, std::filesystem::copy_options::overwrite_existing);

        // 重命名临时文件以替换原始文件
        std::filesystem::rename(tmpPath, filePath);

        // 成功
        return true;
    }
    catch (const std::filesystem::filesystem_error &e)
    {
        // 处理错误
        std::cerr << "Error breaking hardlink: " << e.what() << std::endl;
        return false;
    }
}

std::string KMBuilderUtils::pathMatchPrefix(const std::string &pattern, const std::string &string)
{
    std::string::size_type patternPos = 0;
    std::string::size_type stringPos = 0;

    // cout << "pattern:" << pattern << "----------" << "string:" << string <<  endl;

    // 去除开头的斜杠
    while (patternPos < pattern.size() && pattern[patternPos] == '/')
        ++patternPos;
    while (stringPos < string.size() && string[stringPos] == '/')
        ++stringPos;

    while (patternPos < pattern.size())
    {
        switch (pattern[patternPos++])
        {
        case '?':
            if (stringPos >= string.size() || string[stringPos] == '/')
                return "";
            ++stringPos;
            break;

        case '*':
        {
            char nextPatternChar = (patternPos < pattern.size()) ? pattern[patternPos] : '\0';

            if (nextPatternChar == '/')
            {
                stringPos = string.find('/', stringPos);
                if (stringPos == std::string::npos)
                    return "";
                break;
            }
            else if (nextPatternChar == '\0')
            {
                stringPos = string.find('/', stringPos);
                return (stringPos != std::string::npos) ? string.substr(0, stringPos) : string;
            }

            while (stringPos < string.size())
            {
                std::string remainingPattern = pattern.substr(patternPos); // 减去之前已经处理的'*'
                std::string remainingString = string.substr(stringPos);
                std::string prefix = KMBuilderUtils::pathMatchPrefix(remainingPattern, remainingString);
                if (!prefix.empty())
                {
                    return string.substr(0, stringPos + prefix.size());
                }
                if (string[stringPos] == '/')
                    break;
                ++stringPos;
            }
            return "";
        }

        default:
            if (stringPos >= string.size() || string[stringPos] != pattern[patternPos - 1])
            {
                return "";
            }

            ++stringPos;

            break;
        }
    }

    // 检查是否匹配到了字符串的结尾或斜杠
    if (stringPos == string.size() || string[stringPos] == '/')
    {
        return string.substr(0, stringPos);
    }

    // 如果没有匹配到整个模式，返回空字符串
    return "";
}

bool KMBuilderUtils::matchesPathPattern(string path, string pattern)
{
    if (pattern[0] != '/')
    {
        path = KMBuilderUtils::getBasename(path);
    }

    string prefix = KMBuilderUtils::pathMatchPrefix(pattern, path);
    if (!prefix.empty())
    {
        return true;
    }
    return false;
}

string KMBuilderUtils::getBasename(string path)
{
    if (path.empty())
        return ".";

    // 去除尾部的 '/' 和 '\'（如果有的话）
    size_t lastSlashIdx = path.find_last_of("/");
    if (lastSlashIdx == std::string::npos || lastSlashIdx == path.size() - 1)
    {
        // 如果路径只包含一个目录（如 "/" 或 "C:\"），或者没有目录（如 "file.txt"），则返回当前目录 "."
        return ".";
    }

    // 返回目录名
    return path.substr(lastSlashIdx + 1, path.size() - 1);
}

string KMBuilderUtils::getDirname(const string &path)
{
    if (path.empty())
        return ".";

    // 去除尾部的 '/' 和 '\'（如果有的话）
    size_t lastSlashIdx = path.find_last_of("/");
    if (lastSlashIdx == std::string::npos || lastSlashIdx == path.size() - 1)
    {
        // 如果路径只包含一个目录（如 "/" 或 "C:\"），或者没有目录（如 "file.txt"），则返回当前目录 "."
        return ".";
    }

    // 返回目录名
    return path.substr(0, lastSlashIdx);
}


/**
 * pathMatchPrefix:
 * @brief : 判断路径string是否匹配pattern前缀，返回匹配结束后的部分字符串
 * @param : [in] pattern, 前缀模式
 * @param : [in] string, 路径
 * @return: 返回匹配结束后的部分字符串，不匹配返回null；
 */
const char *KMBuilderUtils::pathMatchPrefix(const char *pattern, const char *string)
{
    char c, test;
    const char *tmp;

    while (*pattern == '/')
    {
        pattern++;
    }

    while (*string == '/')
    {
        string++;
    }

    while (true)
    {
        switch (c = *pattern++)
        {
        case 0:
            if (*string == '/' || *string == 0)
            {
                return string;
            }
            return nullptr;

        case '?':
            if (*string == '/' || *string == 0)
            {
                return nullptr;
            }
            string++;
            break;

        case '*':
            c = *pattern;

            while (c == '*')
            {
                c = *++pattern;
            }

            /* special case * at end */
            if (c == 0)
            {
                tmp = strchr(string, '/');
                if (tmp != nullptr)
                {
                    return tmp;
                }
                return string + strlen(string);
            }
            else if (c == '/')
            {
                string = strchr(string, '/');
                if (string == nullptr)
                {
                    return nullptr;
                }
                break;
            }

            while ((test = *string) != 0)
            {
                tmp = KMBuilderUtils::pathMatchPrefix(pattern, string);
                if (tmp != nullptr)
                {
                    return tmp;
                }
                if (test == '/')
                {
                    break;
                }
                string++;
            }
            return nullptr;

        default:
            if (c != *string)
            {
                return nullptr;
            }
            string++;
            break;
        }
    }

    return nullptr; /* Should not be reached */
}

string KMBuilderUtils::getChildDir(const string &base, const string &child)
{
    string path = base + "/" + child;
    return path;
}

std::string KMBuilderUtils::fileGetContents(const std::string &filePath)
{
    std::ifstream file(filePath, std::ios::in | std::ios::binary);
    if (!file)
    {
        std::cerr << "无法打开文件: " << filePath << std::endl;
        return "";
    }

    std::stringstream buffer;
    buffer << file.rdbuf(); // 将文件内容读入stringstream

    return buffer.str(); // 从stringstream获取字符串
}

bool KMBuilderUtils::fileSetContents(const string &filePath, const string &content)
{
    // std::ofstream file(filePath, std::ios::out | std::ios::trunc); // 清空文件并从头开始写入
    //  或者
    std::ofstream file(filePath, std::ios::app); // 在文件末尾追加数据

    // 检查文件是否成功打开
    if (!file.is_open())
    {
        std::cerr << "无法打开文件: " << filePath << std::endl;
        return false;
    }

    // 写入内容到文件
    file << content;

    // 关闭文件
    file.close();

    std::cout << "内容已写入文件: " << filePath << std::endl;

    return true;
}

bool KMBuilderUtils::fileReSetContents(const string &filePath, const string &content)
{
    std::ofstream file(filePath, std::ios::out | std::ios::trunc); // 清空文件并从头开始写入

    // 检查文件是否成功打开
    if (!file.is_open())
    {
        std::cerr << "无法打开文件: " << filePath << std::endl;
        return false;
    }

    // 写入内容到文件
    file << content;

    // 关闭文件
    file.close();

    std::cout << "内容已写入文件: " << filePath << std::endl;

    return true;
}

// 子进程输出读取线程
void KMBuilderUtils::readSubprocessOutput(int fd, std::ostream &os)
{
    char buffer[1024];
    ssize_t n;
    while ((n = read(fd, buffer, sizeof(buffer) - 1)) > 0)
    {
        buffer[n] = '\0';
        os << buffer;
    }
    // 确保所有的输出都被刷新
    os.flush();
    close(fd);
}

// 函数用于将 vector<string> 转换为 char* 数组
char **KMBuilderUtils::vecToArgv(const std::vector<std::string> &vec, int &argc)
{
    argc = vec.size();
    char **argv = new char *[argc + 1]; // +1 for NULL terminator
    for (int i = 0; i < argc; ++i)
    {
        argv[i] = strdup(vec[i].c_str()); // strdup() 分配内存并复制字符串
        if (argv[i] == NULL)
        {
            // 错误处理...
            std::cerr << "Memory allocation failed!" << std::endl;
            // 释放之前分配的内存...
            for (int j = 0; j < i; ++j)
            {
                free(argv[j]);
            }
            delete[] argv;
            return NULL;
        }
    }
    argv[argc] = NULL; // NULL terminator
    return argv;
}

// spawnv的函数
bool KMBuilderUtils::spawnv(const std::filesystem::path &dir, string &output, int flags, const std::vector<string> &args)
{
    int fds[2];
    if (pipe(fds) == -1)
    {
        perror("pipe");
        return false;
    }

    pid_t pid = fork();
    if (pid == -1)
    {
        perror("fork");
        close(fds[0]);
        close(fds[1]);
        return false;
    }

    if (pid == 0)
    { // 子进程
        // 重定向标准输出到管道
        dup2(fds[1], STDOUT_FILENO);
        close(fds[0]);

        // 如果指定了工作目录，则切换
        if (!dir.empty())
        {
            if (chdir(dir.c_str()) != 0)
            {
                _exit(EXIT_FAILURE);
            }
        }

        // 执行命令
        int argc;
        char **argv = vecToArgv(args, argc);
        if (argv == NULL)
        {
            return false; // 错误处理...
        }

        // 使用 execvp 执行程序
        execvp(args[0].c_str(), argv);

        // 如果 execvp 返回，则意味着出错（通常不会发生，除非某些资源不足）
        perror("execvp failed");

        // 释放之前分配的内存
        for (int i = 0; i < argc; ++i)
        {
            free(argv[i]);
        }
        delete[] argv;
    }

    // 父进程
    close(fds[1]); // 关闭写端

    std::ostringstream streamOutput;
    std::thread readerThread(readSubprocessOutput, fds[0], std::ref(streamOutput));

    // 等待子进程退出
    int status;
    waitpid(pid, &status, 0);

    readerThread.join(); // 等待读取线程完成
    output = streamOutput.str();

    if (WIFEXITED(status))
    {
        return true;
    }
    else
    {
        return false;
    }
}

string KMBuilderUtils::kaiming(int type, ...)
{
    bool res;
    string output = "";
    vector<string> args;

    /* 处理附加参数*/
    va_list ap;

    string arg;
    args.push_back("kaiming");
    va_start(ap, type);
    while (!(arg = va_arg(ap, const char *)).empty())
    {
        args.push_back(arg);
    }
    va_end(ap);

    if (KMBuilderUtils::isInSanbox())
    {
        if (!KMBuilderUtils::hostSpawn("", 0, args))
        {
            // km_logger.info("Error hostSpawn");
            return "";
        }
        else
        {
            return output;
        }
    }
    else
    {
        if (!KMBuilderUtils::spawnv("", output, 0, args))
        {
            // km_logger.info("Error spawnv");
            return "";
        }
        else
        {
            return output;
        }
    }
}

bool KMBuilderUtils::loadIniData(const std::string &data, pt::ptree &pt)
{
    std::istringstream iss(data);
    try
    {
        pt::read_ini(iss, pt);
    }
    catch (const pt::ini_parser_error &e)
    {
        std::cerr << "INI parsing error: " << e.what() << std::endl;
        return false;
    }
    return true;
}

std::string KMBuilderUtils::ptreeToIniString(const pt::ptree &pt)
{
    std::ostringstream oss;
    write_ini(oss, pt);
    return oss.str();
}

bool KMBuilderUtils::loadIniFile(const std::string &filename, pt::ptree &pt)
{
    std::ifstream ifs(filename);
    if (!ifs)
    {
        std::cerr << "Unable to open file !" << filename << std::endl;
        return false;
    }
    try
    {
        pt::read_ini(ifs, pt);
    }
    catch (const pt::ini_parser_error &e)
    {
        std::cerr << "INI parsing error: " << e.what() << std::endl;
        return false;
    }
    return true;
}

bool KMBuilderUtils::hasGroup(const pt::ptree &pt, const std::string &group)
{
    return pt.find(group) != pt.not_found();
}

bool KMBuilderUtils::groupHasKey(const pt::ptree &pt, const std::string &group, const std::string &key)
{
    // 查找名为groupName的子树
    auto groupIt = pt.find(group);
    if (groupIt != pt.not_found())
    { // 如果找到了指定的组
        // 在组内查找keyName
        auto keyIt = groupIt->second.find(key);
        if (keyIt != groupIt->second.not_found())
        {
            // 如果找到了指定的键，返回true
            return true;
        }
    }
    // 如果没有找到指定的组或键，返回false
    return false;
}

vector<string> KMBuilderUtils::getAllGroupNames(const boost::property_tree::ptree &pt)
{
    std::vector<std::string> groupNames;
    for (const auto &pair : pt)
    {
        // pair.first 是键，pair.second 是对应的值（可能是一个子树）
        groupNames.push_back(pair.first);
    }
    return groupNames;
}

void KMBuilderUtils::removeGroup(boost::property_tree::ptree &pt, const std::string &group)
{
    // 遍历顶层节点
    for (auto it = pt.begin(); it != pt.end();)
    {
        // 如果当前节点的键等于要移除的组名，则擦除它
        if (it->first == group)
        {
            it = pt.erase(it);
        }
        else
        {
            // 否则，继续下一个节点
            ++it;
        }
    }
}

std::vector<std::string> KMBuilderUtils::getKeys(const pt::ptree &groupPt, const std::string &groupName)
{
    std::vector<std::string> keys;
    // 查找名为groupName的子树
    auto groupIt = groupPt.find(groupName);
    if (groupIt != groupPt.not_found())
    { // 如果找到了指定的组
        // 遍历该组的所有键
        for (const auto &pair : groupIt->second)
        {
            keys.push_back(pair.first);
        }
    }
    // 如果没有找到指定的组，keys将是一个空向量
    return keys;
}

std::vector<std::string> KMBuilderUtils::getStringListFromPt(pt::ptree &groupPt, const string &groupName, const string &key)
{
    std::vector<std::string> result;
    // 假设键的值是一个逗号分隔的字符串列表
    auto value = groupPt.get_optional<std::string>(groupName + "." + key);
    if (value)
    {
        std::string strList = *value;
        size_t pos = 0;
        std::string token;
        while ((pos = strList.find(';')) != std::string::npos)
        {
            token = strList.substr(0, pos);
            result.push_back(token);
            strList.erase(0, pos + 1);
        }
        // 添加最后一个元素（如果没有逗号结尾）
        if (!strList.empty())
        {
            result.push_back(strList);
        }
    }

    return result;
}

void KMBuilderUtils::writeStringListToPt(pt::ptree &groupPt, const std::string &groupName, const std::string &key, const std::vector<std::string> &stringList)
{
    std::ostringstream oss;
    for (size_t i = 0; i < stringList.size(); ++i)
    {
        oss << stringList[i];
        if (i < stringList.size() - 1)
        {
            oss << ";";
        }
    }
    // 添加或覆盖指定组和键下的值
    groupPt.put(groupName + "." + key, oss.str());
}

std::string KMBuilderUtils::getValue(const pt::ptree &pt, const std::string &group, const std::string &key)
{
    return pt.get<std::string>(group + "." + key, "");
}

void KMBuilderUtils::setValue(pt::ptree &pt, const std::string &group, const std::string &key, const std::string &value)
{
    pt.put(group + "." + key, value);
}

bool KMBuilderUtils::saveToIniFile(const pt::ptree &pt, const std::string &filename)
{
    std::ofstream file(filename);
    if (!file)
    {
        std::cerr << "Could not open file for writing: " << filename << std::endl;
        return false;
    }

    // 使用INI解析器将ptree对象写入文件
    pt::write_ini(file, pt);

    // 确保所有输出都被刷新到文件
    file.flush();
    return true;
}

vector<string> KMBuilderUtils::getKeysAsVector(const std::unordered_map<string, bool> &map)
{
    vector<string> keys;
    for (const auto &pair : map)
    {
        keys.push_back(pair.first);
    }
    return keys;
}

bool KMBuilderUtils::ifContains(const std::unordered_map<string, bool> &map, string key)
{
    for (const auto &pair : map)
    {
        if (pair.first == key)
        {
            return true;
        }
    }
    return false;
}

vector<string> KMBuilderUtils::enumerateDirectoryPath(const string &path, bool ignoreSymlink)
{
    vector<string> files;

    // 检查路径是否存在
    if (!fs::exists(path) || !fs::is_directory(path))
    {
        km_logger.info("Path does not exist or is not a directory :%s !", path.c_str());
        return files;
    }

    // 遍历目录中的文件和子目录
    for (const auto &entry : fs::directory_iterator(path))
    {
        // 只列举文件（不包括子目录）可以添加额外的检查：entry.is_regular_file()
        if (ignoreSymlink && fs::is_symlink(entry.path()))
            continue;
        files.push_back(entry.path().string());
    }

    return files;
}

vector<string> KMBuilderUtils::concatVectors(const vector<string> &vec1, const vector<string> &vec2)
{
    std::vector<std::string> result;
    result.reserve(vec1.size() + vec2.size()); // 预分配内存以提高效率

    // 将vec1的内容添加到result中
    result.insert(result.end(), vec1.begin(), vec1.end());

    // 将vec2的内容添加到result中
    result.insert(result.end(), vec2.begin(), vec2.end());

    return result;
}

std::string KMBuilderUtils::getSymlinkTarget(const std::string &path)
{
    fs::path symlinkPath(path);
    int maxLen = 1024;
    std::string targetPath;
    char buffer[maxLen]; // 足够大的缓冲区来存储路径
    ssize_t len = readlink(symlinkPath.c_str(), buffer, maxLen - 1);

    if (len != -1)
    {
        // readlink不包含最后的null字符，所以我们手动添加它
        buffer[len] = '\0';
        targetPath = buffer;
        // 可能需要解析路径中的任何相对组件
        targetPath = std::filesystem::absolute(targetPath);
        // targetPath = std::filesystem::absolute(targetPath, symlinkPath.parentPath());
    }
    else
    {
        // 处理错误（例如，如果路径不是符号链接）
        perror("readlink");
    }

    return targetPath;
}

bool KMBuilderUtils::isStringSymlink(const std::string &pathString)
{
    fs::path path(pathString);

    fs::file_status status = fs::symlink_status(path);
    return fs::is_symlink(status);
}

bool KMBuilderUtils::migrateLocaleDirs(string rootDir)
{
    string libLocaleDir = rootDir + "/" + "lib/locale";
    string shareLocaleDir = rootDir + "/" + "share/locale";
    string separateDir = rootDir + "/" + "share/runtime/locale";

    if (!migrateLocaleDir(libLocaleDir, separateDir, "lib"))
    {
        return false;
    }

    if (!migrateLocaleDir(shareLocaleDir, separateDir, "share"))
    {
        return false;
    }
    return true;
}

bool KMBuilderUtils::migrateLocaleDir(string sourceDir, string separateDir, string subdir)
{
    vector<string> dirEnum = KMBuilderUtils::enumerateDirectoryPath(sourceDir, true);
    if (dirEnum.empty())
    {
        return true;
    }
    for (auto fileName : dirEnum)
    {
        fs::path filePath(fileName);
        // 检查路径是否存在且是一个目录
        if (fs::exists(filePath) && fs::is_directory(filePath))
        {
            string name = filePath.filename().string();
            string language;

            // 清理目录名（移除@、_和.）
            for (char c : name)
            {
                if (c == '@' || c == '_' || c == '.')
                    break;
                language.push_back(c);
            }

            // 跳过"C"和"en"本地化
            if (language == "C" || language == "en")
                continue;

            string localeSubdir = separateDir + string("/") + language + "/" + subdir + "/" + name;

            std::error_code ec;
            if (!fs::exists(localeSubdir, ec) && !fs::create_directories(localeSubdir, ec))
                return false;

            KMBuilderGlobal global;
            vector<string> skipFiles;
            if (!global.cpAll(fileName, localeSubdir, "", static_cast<KaimingCpFlags>(KAIMING_CP_FLAGS_MERGE | KAIMING_CP_FLAGS_MOVE), skipFiles))
            {
                return false;
            }

            // 创建符号链接
            string target = string("../../share/runtime/locale/") + language + string("/") + subdir + string("/") + name;
            if (!createDirectorySymlink(target, fileName, ec))
            {
                // 处理错误
                std::cerr << "Failed to create symbolic link: " << fileName << std::endl;
                return false;
            }
            else
            {
                std::cout << "Symbolic link created successfully." << std::endl;
            }
        }
    }

    return true;
}
