mirror of
https://github.com/Tiiffi/mcrcon.git
synced 2025-12-17 02:32:54 -05:00
Refactor color handling and add error handling to UTF-8 code
This commit is contained in:
123
mcrcon.c
123
mcrcon.c
@@ -30,6 +30,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -456,6 +457,69 @@ rc_packet *net_recv_packet(int sd)
|
|||||||
return &packet;
|
return &packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NOTE: At least Bukkit/Spigot servers are sending formatting via rcon but not sure how common this
|
||||||
|
* behaviour is. This implementation tries to print colors but is omitting "bold" in "bright" colors
|
||||||
|
* because bold tag should be manually reset and Bukkit/Spigot is not doing this.
|
||||||
|
* References: https://minecraft.fandom.com/wiki/Formatting_codes
|
||||||
|
* https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
|
*/
|
||||||
|
void set_color(int c)
|
||||||
|
{
|
||||||
|
c = tolower(c);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
// Map color Minecraft color codes to WinAPI colors
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
c -= '0';
|
||||||
|
}
|
||||||
|
else if (c >= 'a' && c <= 'f') {
|
||||||
|
c -= 87;
|
||||||
|
}
|
||||||
|
else return;
|
||||||
|
|
||||||
|
SetConsoleTextAttribute(console_handle, c);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* NOTE: Using "bold" for bright colors for now. This is not correct exactly
|
||||||
|
* because it resets other formattings and must be reseted manually.
|
||||||
|
* This should match Bukkit/Spigot server console output though
|
||||||
|
*/
|
||||||
|
const char *ansi_escape;
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '0': ansi_escape = "\033[30m"; break; // Black
|
||||||
|
case '1': ansi_escape = "\033[34m"; break; // Blue
|
||||||
|
case '2': ansi_escape = "\033[32m"; break; // Green
|
||||||
|
case '3': ansi_escape = "\033[36m"; break; // Cyan
|
||||||
|
case '4': ansi_escape = "\033[31m"; break; // Red
|
||||||
|
case '5': ansi_escape = "\033[35m"; break; // Magenta
|
||||||
|
case '6': ansi_escape = "\033[33m"; break; // Yellow
|
||||||
|
case '7': ansi_escape = "\033[37m"; break; // White
|
||||||
|
case '8': ansi_escape = "\033[1;90m"; break; // Bright black (gray)
|
||||||
|
case '9': ansi_escape = "\033[1;94m"; break; // Bright blue
|
||||||
|
case 'a': ansi_escape = "\033[1;92m"; break; // Bright green
|
||||||
|
case 'b': ansi_escape = "\033[1;96m"; break; // Bright cyan
|
||||||
|
case 'c': ansi_escape = "\033[1;91m"; break; // Bright red
|
||||||
|
case 'd': ansi_escape = "\033[1;95m"; break; // Bright magenta
|
||||||
|
case 'e': ansi_escape = "\033[1;93m"; break; // Bright yellow
|
||||||
|
case 'f': ansi_escape = "\033[1;97m"; break; // Bright white
|
||||||
|
case 'k': ansi_escape = "\033[08m"; break; // Concealed text
|
||||||
|
case 'l': ansi_escape = "\033[01m"; break; // Bold text
|
||||||
|
case 'm': ansi_escape = "\033[09m"; break; // Strikethrough text
|
||||||
|
case 'n': ansi_escape = "\033[03m"; break; // Italic text
|
||||||
|
case 'o': ansi_escape = "\033[04m"; break; // Underlined text
|
||||||
|
case 0: // fallthrough
|
||||||
|
case 'r': ansi_escape = "\033[00m"; break; // Reset all attributes
|
||||||
|
default: return;
|
||||||
|
|
||||||
|
}
|
||||||
|
fputs(ansi_escape, stdout);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Old version!
|
||||||
void print_color(int color)
|
void print_color(int color)
|
||||||
{
|
{
|
||||||
// sh compatible color array
|
// sh compatible color array
|
||||||
@@ -480,11 +544,11 @@ void print_color(int color)
|
|||||||
"\033[4m" /* 16 UNDERLINE 0x6e */
|
"\033[4m" /* 16 UNDERLINE 0x6e */
|
||||||
};
|
};
|
||||||
|
|
||||||
if (color == 0 || color == 0x72) {
|
if (color == 0 || color == 'r') {
|
||||||
fputs("\033[0m", stdout); // cancel color
|
fputs("\033[0m", stdout); // cancel color
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (color >= 0x61 && color <= 0x66) color -= 0x57;
|
if (color >= 0x61 && color <= 0x66) color -= 0x57;
|
||||||
else if (color >= 0x30 && color <= 0x39)
|
else if (color >= 0x30 && color <= 0x39)
|
||||||
@@ -497,7 +561,7 @@ void print_color(int color)
|
|||||||
fputs(colors[color], stdout);
|
fputs(colors[color], stdout);
|
||||||
#else
|
#else
|
||||||
SetConsoleTextAttribute(console_handle, color);
|
SetConsoleTextAttribute(console_handle, color);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,7 +587,7 @@ void packet_print(rc_packet *packet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int default_color = 0;
|
int default_color = 'r';
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
CONSOLE_SCREEN_BUFFER_INFO console_info;
|
CONSOLE_SCREEN_BUFFER_INFO console_info;
|
||||||
@@ -538,15 +602,14 @@ void packet_print(rc_packet *packet)
|
|||||||
|
|
||||||
for (i = 0; data[i] != 0; ++i)
|
for (i = 0; data[i] != 0; ++i)
|
||||||
{
|
{
|
||||||
if (data[i] == 0x0A) {
|
// Check for '§' (utf-8 section sign). Bukkit is using this for colors.
|
||||||
print_color(default_color);
|
if(data[i] == 0xc2 && data[i + 1] == 0xa7) {
|
||||||
}
|
|
||||||
else if(data[i] == 0xc2 && data[i + 1] == 0xa7) {
|
|
||||||
// Disable new line fixes if Bukkit colors are detected
|
// Disable new line fixes if Bukkit colors are detected
|
||||||
colors_detected = true;
|
colors_detected = true;
|
||||||
i += 2;
|
i += 2;
|
||||||
if (flag_disable_colors == 0) {
|
if (flag_disable_colors == 0) {
|
||||||
print_color(data[i]);
|
//print_color(data[i]);
|
||||||
|
set_color(data[i]);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -558,7 +621,9 @@ void packet_print(rc_packet *packet)
|
|||||||
|
|
||||||
putchar(data[i]);
|
putchar(data[i]);
|
||||||
}
|
}
|
||||||
print_color(default_color); // cancel coloring
|
|
||||||
|
set_color(default_color); // reset color
|
||||||
|
//print_color(default_color); // cancel coloring
|
||||||
|
|
||||||
// print newline if string has no newline
|
// print newline if string has no newline
|
||||||
if (data[i - 1] != '\n') {
|
if (data[i - 1] != '\n') {
|
||||||
@@ -770,7 +835,6 @@ int run_terminal_mode(int sock)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char *utf8_getline(char *buf, int size, FILE *stream)
|
char *utf8_getline(char *buf, int size, FILE *stream)
|
||||||
{
|
{
|
||||||
// Widechar fgets
|
|
||||||
wchar_t in[MAX_COMMAND_LENGTH] = {0};
|
wchar_t in[MAX_COMMAND_LENGTH] = {0};
|
||||||
|
|
||||||
wchar_t *result = fgetws(in, MAX_COMMAND_LENGTH, stream);
|
wchar_t *result = fgetws(in, MAX_COMMAND_LENGTH, stream);
|
||||||
@@ -778,34 +842,27 @@ char *utf8_getline(char *buf, int size, FILE *stream)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates UTF-8 buffer size
|
size_t length = wcslen(in);
|
||||||
|
if (length > 0 && in[length - 1] == L'\n') {
|
||||||
|
in[length - 1] = L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate UTF-8 buffer size
|
||||||
int required_size = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
|
int required_size = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
|
||||||
if (size < required_size) {
|
if (required_size > size || required_size == 0) {
|
||||||
// TODO: Proper error handling & reporting
|
log_error("Widechar to UTF-8 conversion failed.\n");
|
||||||
return NULL;
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WideCharToMultiByte(CP_UTF8, 0, in, -1, buf, size, NULL, NULL) == 0) {
|
if (WideCharToMultiByte(CP_UTF8, 0, in, -1, buf, size, NULL, NULL) == 0) {
|
||||||
// TODO: Proper error handling & reporting
|
log_error("Widechar to UTF-8 conversion failed.\n");
|
||||||
return NULL;
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
void flush_input(void)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
// NOTE: Undefined behaviour in C standard but Windows allows it
|
|
||||||
fflush(stdin);
|
|
||||||
#else
|
|
||||||
__fpurge(stdin);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// gets line from stdin and deals with rubbish left in the input buffer
|
// gets line from stdin and deals with rubbish left in the input buffer
|
||||||
int get_line(char *buffer, int bsize)
|
int get_line(char *buffer, int bsize)
|
||||||
{
|
{
|
||||||
@@ -820,23 +877,19 @@ int get_line(char *buffer, int bsize)
|
|||||||
log_error("Error %d: %s\n", errno, strerror(errno));
|
log_error("Error %d: %s\n", errno, strerror(errno));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
// EOF
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove unwanted characters from the buffer
|
// remove unwanted characters from the buffer
|
||||||
buffer[strcspn(buffer, "\r\n")] = '\0';
|
buffer[strcspn(buffer, "\r\n")] = '\0';
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
// NOTE: Undefined behaviour in C standard but Windows allows it
|
|
||||||
fflush(stdin);
|
|
||||||
#else
|
|
||||||
int len = strlen(buffer);
|
int len = strlen(buffer);
|
||||||
|
|
||||||
if (len == bsize - 1) {
|
if (len == bsize - 1) {
|
||||||
int ch;
|
int ch;
|
||||||
while ((ch = getchar()) != '\n' && ch != EOF);
|
while ((ch = getchar()) != '\n' && ch != EOF);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user