/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* 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, see .
*/
/// \addtogroup Trinityd
/// @{
/// \file
#include "CliRunnable.h"
#include "Config.h"
#include "Util.h"
#include "World.h"
#if TRINITY_PLATFORM != TRINITY_PLATFORM_WINDOWS
#include "Chat.h"
#include "ChatCommand.h"
#include
#include
#else
#include
#endif
static constexpr char CLI_PREFIX[] = "TC> ";
static inline void PrintCliPrefix()
{
printf("%s", CLI_PREFIX);
}
#if TRINITY_PLATFORM != TRINITY_PLATFORM_WINDOWS
namespace Trinity::Impl::Readline
{
static std::vector vec;
char* cli_unpack_vector(char const*, int state)
{
static size_t i=0;
if (!state)
i = 0;
if (i < vec.size())
return strdup(vec[i++].c_str());
else
return nullptr;
}
char** cli_completion(char const* text, int /*start*/, int /*end*/)
{
::rl_attempted_completion_over = 1;
vec = Trinity::ChatCommands::GetAutoCompletionsFor(CliHandler(nullptr,nullptr), text);
return ::rl_completion_matches(text, &cli_unpack_vector);
}
int cli_hook_func()
{
if (World::IsStopped())
::rl_done = 1;
return 0;
}
}
#endif
void utf8print(void* /*arg*/, std::string_view str)
{
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
WriteWinConsole(str);
#else
{
printf(STRING_VIEW_FMT, STRING_VIEW_FMT_ARG(str));
fflush(stdout);
}
#endif
}
void commandFinished(void*, bool /*success*/)
{
PrintCliPrefix();
fflush(stdout);
}
#ifdef linux
// Non-blocking keypress detector, when return pressed, return 1, else always return 0
int kb_hit_return()
{
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, nullptr, nullptr, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
#endif
/// %Thread start
void CliThread()
{
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
// print this here the first time
// later it will be printed after command queue updates
PrintCliPrefix();
#else
::rl_attempted_completion_function = &Trinity::Impl::Readline::cli_completion;
{
static char BLANK = '\0';
::rl_completer_word_break_characters = &BLANK;
}
::rl_event_hook = &Trinity::Impl::Readline::cli_hook_func;
#endif
if (sConfigMgr->GetBoolDefault("BeepAtStart", true))
printf("\a"); // \a = Alert
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
if (sConfigMgr->GetBoolDefault("FlashAtStart", true))
{
FLASHWINFO fInfo;
fInfo.cbSize = sizeof(FLASHWINFO);
fInfo.dwFlags = FLASHW_TRAY | FLASHW_TIMERNOFG;
fInfo.hwnd = GetConsoleWindow();
fInfo.uCount = 0;
fInfo.dwTimeout = 0;
FlashWindowEx(&fInfo);
}
#endif
///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
while (!World::IsStopped())
{
fflush(stdout);
std::string command;
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
if (!ReadWinConsole(command))
continue;
#else
char* command_str = readline(CLI_PREFIX);
::rl_bind_key('\t', ::rl_complete);
if (command_str != nullptr)
{
command = command_str;
free(command_str);
}
#endif
if (!command.empty())
{
Optional nextLineIndex = RemoveCRLF(command);
if (nextLineIndex && *nextLineIndex == 0)
{
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
PrintCliPrefix();
#endif
continue;
}
fflush(stdout);
sWorld->QueueCliCommand(new CliCommandHolder(nullptr, command.c_str(), &utf8print, &commandFinished));
#if TRINITY_PLATFORM != TRINITY_PLATFORM_WINDOWS
add_history(command.c_str());
#endif
}
else if (feof(stdin))
{
World::StopNow(SHUTDOWN_EXIT_CODE);
}
}
}