OSDN Git Service

Fixed a few more instances of "LameXP" being hardcoded into MUtilities library.
[mutilities/MUtilities.git] / src / Terminal_Win32.cpp
index 9f1b404..ceb2530 100644 (file)
@@ -1,6 +1,6 @@
 ///////////////////////////////////////////////////////////////////////////////
-// LameXP - Audio Encoder Front-End
-// Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
+// MuldeR's Utilities for Qt
+// Copyright (C) 2004-2016 LoRd_MuldeR <MuldeR2@GMX.de>
 //
 // 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
 #include <QFile>
 #include <QStringList>
 #include <QIcon>
-#include <QLibrary>
 
 //CRT
 #include <iostream>
 #include <fstream>
+#include <ctime>
+#include <stdarg.h>
 #include <io.h>
 #include <fcntl.h>
-#include <ctime>
-
-//Lock
-static MUtils::Internal::CriticalSection g_terminal_lock;
 
 #ifdef _MSC_VER
 #define stricmp(X,Y) _stricmp((X),(Y))
 #endif
 
+#define VALID_HANLDE(X) (((X) != NULL) && ((X) != INVALID_HANDLE_VALUE))
+
+///////////////////////////////////////////////////////////////////////////////
+// TERMINAL VARIABLES
+///////////////////////////////////////////////////////////////////////////////
+
+//Critical section
+static MUtils::Internal::CriticalSection g_terminal_lock;
+
+//Is terminal attached?
+static volatile bool g_terminal_attached = false;
+
+//Terminal output buffer
+static const size_t BUFF_SIZE = 8192;
+static char g_conOutBuff[BUFF_SIZE] = { '\0' };
+
+//Buffer objects
+static QScopedPointer<std::filebuf> g_fileBuf_stdout;
+static QScopedPointer<std::filebuf> g_fileBuf_stderr;
+
+//The log file
+static QScopedPointer<QFile> g_terminal_log_file;
+
 ///////////////////////////////////////////////////////////////////////////////
 // HELPER FUNCTIONS
 ///////////////////////////////////////////////////////////////////////////////
 
-static void make_timestamp(char *timestamp, const size_t &buffsize)
+static inline void make_timestamp(char *timestamp, const size_t &buffsize)
 {
        time_t rawtime;
        struct tm timeinfo;
@@ -64,7 +84,7 @@ static void make_timestamp(char *timestamp, const size_t &buffsize)
        time(&rawtime);
        if(localtime_s(&timeinfo, &rawtime) == 0)
        {
-               strftime(timestamp, 32, "%H:%M:%S", &timeinfo);
+               strftime(timestamp, buffsize, "%H:%M:%S", &timeinfo);
        }
        else
        {
@@ -72,46 +92,91 @@ static void make_timestamp(char *timestamp, const size_t &buffsize)
        }
 }
 
-static const char *clean_str(char *str)
+static inline bool null_or_whitespace(const char *const str)
 {
-       //Clean
-       char *ptr = &str[0];
-       while((*ptr) && isspace(*ptr))
+       if (str)
        {
-               *(ptr++) = 0x20;
+               size_t pos = 0;
+               while (str[pos])
+               {
+                       if (!(isspace(str[pos]) || iscntrl(str[pos])))
+                       {
+                               return false;
+                       }
+                       pos++;
+               }
        }
+       return true;
+}
 
-       //Trim left
-       while((*str) && isspace(*str))
-       {
-               str++;
-       }
+static inline size_t clean_string(char *const str)
+{
+       bool space_flag = true;
+       size_t src = 0, out = 0;
 
-       //Trim right
-       size_t pos = strlen(str);
-       while(pos > 0)
+       while (str[src])
        {
-               if(isspace(str[--pos]))
+               if (isspace(str[src]) || iscntrl(str[src])) /*replace any space-sequence with a single space character*/
                {
-                       str[pos] = '\0';
-                       continue;
+                       src++;
+                       if (!space_flag)
+                       {
+                               space_flag = true;
+                               str[out++] = 0x20;
+                       }
+               }
+               else /*otherwise we'll just copy over the current character*/
+               {
+                       if (src != out)
+                       {
+                               str[out] = str[src];
+                       }
+                       space_flag = false;
+                       out++; src++;
                }
-               break;
        }
 
-       return str;
+       if (space_flag && (out > 0)) /*trim trailing space, if any*/
+       {
+               out--;
+       }
+
+       str[out] = NULL;
+       return out;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // TERMINAL SETUP
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool g_terminal_attached = false;
-static QScopedPointer<std::filebuf> g_filebufStdOut;
-static QScopedPointer<std::filebuf> g_filebufStdErr;
-static QScopedPointer<QFile> g_log_file;
+static inline std::filebuf *terminal_connect(FILE *const fs, std::ostream &os)
+{
+       std::filebuf *result = NULL;
+       FILE *temp;
+       if (freopen_s(&temp, "CONOUT$", "wb", fs) == 0)
+       {
+               os.rdbuf(result = new std::filebuf(temp));
+       }
+       return result;
+}
+
+static void terminal_shutdown(void)
+{
+       MUtils::Internal::CSLocker lock(g_terminal_lock);
+
+       if (g_terminal_attached)
+       {
+               g_fileBuf_stdout.reset();
+               g_fileBuf_stderr.reset();
+               FILE *temp[2];
+               if(stdout) freopen_s(&temp[0], "NUL", "wb", stdout);
+               if(stderr) freopen_s(&temp[1], "NUL", "wb", stderr);
+               FreeConsole();
+               g_terminal_attached = false;
+       }
+}
 
-void MUtils::Terminal::setup(int &argc, char **argv, const bool forceEnabled)
+void MUtils::Terminal::setup(int &argc, char **argv, const char* const appName, const bool forceEnabled)
 {
        MUtils::Internal::CSLocker lock(g_terminal_lock);
        bool enableConsole = (MUTILS_DEBUG) || forceEnabled;
@@ -123,11 +188,11 @@ void MUtils::Terminal::setup(int &argc, char **argv, const bool forceEnabled)
                {
                        if(logfile && (logfile_len > 0))
                        {
-                               g_log_file.reset(new QFile(MUTILS_QSTR(logfile)));
-                               if(g_log_file->open(QIODevice::WriteOnly))
+                               g_terminal_log_file.reset(new QFile(MUTILS_QSTR(logfile)));
+                               if(g_terminal_log_file->open(QIODevice::WriteOnly))
                                {
                                        static const char MARKER[3] = { char(0xEF), char(0xBB), char(0xBF) };
-                                       g_log_file->write(MARKER, 3);
+                                       g_terminal_log_file->write(MARKER, 3);
                                }
                                free(logfile);
                        }
@@ -155,36 +220,24 @@ void MUtils::Terminal::setup(int &argc, char **argv, const bool forceEnabled)
                {
                        if(AllocConsole() != FALSE)
                        {
-                               SetConsoleCtrlHandler(NULL, TRUE);
-                               SetConsoleTitle(L"LameXP - Audio Encoder Front-End | Debug Console");
                                SetConsoleOutputCP(CP_UTF8);
+                               SetConsoleCtrlHandler(NULL, TRUE);
+                               if(appName && appName[0])
+                               {
+                                       char title[128];
+                                       _snprintf_s(title, 128, _TRUNCATE, "%s | Debug Console", appName);
+                                       SetConsoleTitleA(title);
+                               }
                                g_terminal_attached = true;
                        }
                }
 
                if(g_terminal_attached)
                {
-                       //-------------------------------------------------------------------
-                       //See: http://support.microsoft.com/default.aspx?scid=kb;en-us;105305
-                       //-------------------------------------------------------------------
-                       const int flags = _O_WRONLY | _O_U8TEXT;
-                       const int hCrtStdOut = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), flags);
-                       const int hCrtStdErr = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE ), flags);
-                       FILE *const hfStdOut = (hCrtStdOut >= 0) ? _fdopen(hCrtStdOut, "wb") : NULL;
-                       FILE *const hfStdErr = (hCrtStdErr >= 0) ? _fdopen(hCrtStdErr, "wb") : NULL;
-                       if(hfStdOut)
-                       {
-                               *stdout = *hfStdOut;
-                               g_filebufStdOut.reset(new std::filebuf(hfStdOut));
-                               std::cout.rdbuf(g_filebufStdOut.data());
-                       }
-                       if(hfStdErr)
-                       {
-                               *stderr = *hfStdErr;
-                               g_filebufStdErr.reset(new std::filebuf(hfStdErr));
-                               std::cerr.rdbuf(g_filebufStdErr.data());
-                               std::cerr.rdbuf(new std::filebuf(hfStdErr));
-                       }
+                       g_fileBuf_stdout.reset(terminal_connect(stdout, std::cout));
+                       g_fileBuf_stderr.reset(terminal_connect(stderr, std::cerr));
+
+                       atexit(terminal_shutdown);
 
                        const HWND hwndConsole = GetConsoleWindow();
                        if((hwndConsole != NULL) && (hwndConsole != INVALID_HANDLE_VALUE))
@@ -211,12 +264,15 @@ static const WORD COLOR_YELLOW = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_
 static const WORD COLOR_WHITE  = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
 static const WORD COLOR_DEFAULT= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
 
-static void set_terminal_color(FILE* file, const WORD &attributes)
+static void set_terminal_color(FILE *const fp, const WORD &attributes)
 {
-       const HANDLE hConsole = (HANDLE)(_get_osfhandle(_fileno(file)));
-       if((hConsole != NULL) && (hConsole != INVALID_HANDLE_VALUE))
+       if(_isatty(_fileno(fp)))
        {
-               SetConsoleTextAttribute(hConsole, attributes);
+               const HANDLE hConsole = (HANDLE)(_get_osfhandle(_fileno(fp)));
+               if (VALID_HANLDE(hConsole))
+               {
+                       SetConsoleTextAttribute(hConsole, attributes);
+               }
        }
 }
 
@@ -227,89 +283,98 @@ static void set_terminal_color(FILE* file, const WORD &attributes)
 static const char *const FORMAT = "[%c][%s] %s\r\n";
 static const char *const GURU_MEDITATION = "\n\nGURU MEDITATION !!!\n\n";
 
-static void write_logfile_helper(QFile *const file, const int &type, const char *const message)
+static void write_to_logfile(QFile *const file, const int &type, const char *const message)
 {
-       static char input[1024], output[1024], timestamp[32];
+       int len = -1;
+
+       if (null_or_whitespace(message))
+       {
+               return; /*don't write empty message to log file*/
+       }
+
+       static char timestamp[32];
        make_timestamp(timestamp, 32);
-       strncpy_s(input, 1024, message, _TRUNCATE);
-       const char *const temp = clean_str(input);
 
        switch(type)
        {
        case QtCriticalMsg:
        case QtFatalMsg:
-               _snprintf_s(output, 1024, FORMAT, 'C', timestamp, temp);
+               len = _snprintf_s(g_conOutBuff, BUFF_SIZE, _TRUNCATE, FORMAT, 'C', timestamp, message);
                break;
        case QtWarningMsg:
-               _snprintf_s(output, 1024, FORMAT, 'W', timestamp, temp);
+               len = _snprintf_s(g_conOutBuff, BUFF_SIZE, _TRUNCATE, FORMAT, 'W', timestamp, message);
                break;
        default:
-               _snprintf_s(output, 1024, FORMAT, 'I', timestamp, temp);
+               len = _snprintf_s(g_conOutBuff, BUFF_SIZE, _TRUNCATE, FORMAT, 'I', timestamp, message);
                break;
        }
 
-       file->write(output);
-       file->flush();
+       if (len > 0)
+       {
+               if (clean_string(g_conOutBuff) > 0)
+               {
+                       file->write(g_conOutBuff);
+                       file->flush();
+               }
+       }
 }
 
-static void write_debugger_helper(const int &type, const char *const message)
+static void write_to_debugger(const int &type, const char *const message)
 {
-       static char input[1024], output[1024], timestamp[32];
+       int len = -1;
+
+       if (null_or_whitespace(message))
+       {
+               return; /*don't send empty message to debugger*/
+       }
+
+       static char timestamp[32];
        make_timestamp(timestamp, 32);
-       strncpy_s(input, 1024, message, _TRUNCATE);
-       const char *const temp = clean_str(input);
 
        switch(type)
        {
        case QtCriticalMsg:
        case QtFatalMsg:
-               _snprintf_s(output, 1024, FORMAT, 'C', timestamp, temp);
+               len = _snprintf_s(g_conOutBuff, BUFF_SIZE, _TRUNCATE, FORMAT, 'C', timestamp, message);
                break;
        case QtWarningMsg:
-               _snprintf_s(output, 1024, FORMAT, 'W', timestamp, temp);
+               len = _snprintf_s(g_conOutBuff, BUFF_SIZE, _TRUNCATE, FORMAT, 'W', timestamp, message);
                break;
        default:
-               _snprintf_s(output, 1024, FORMAT, 'I', timestamp, temp);
+               len = _snprintf_s(g_conOutBuff, BUFF_SIZE, _TRUNCATE, FORMAT, 'I', timestamp, message);
                break;
        }
 
-       OutputDebugStringA(output);
+       if (len > 0)
+       {
+               if (clean_string(g_conOutBuff) > 0)
+               {
+                       OutputDebugStringA(g_conOutBuff);
+               }
+       }
 }
 
-static void write_terminal_helper(const int &type, const char *const message)
+static void write_to_terminal(const int &type, const char *const message)
 {
-       if(_isatty(_fileno(stderr)))
+       switch(type)
        {
-               UINT oldOutputCP = GetConsoleOutputCP();
-               if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
-
-               switch(type)
-               {
-               case QtCriticalMsg:
-               case QtFatalMsg:
-                       set_terminal_color(stderr, COLOR_RED);
-                       fprintf(stderr, GURU_MEDITATION);
-                       fprintf(stderr, "%s\n", message);
-                       fflush(stderr);
-                       break;
-               case QtWarningMsg:
-                       set_terminal_color(stderr, COLOR_YELLOW);
-                       fprintf(stderr, "%s\n", message);
-                       fflush(stderr);
-                       break;
-               default:
-                       set_terminal_color(stderr, COLOR_WHITE);
-                       fprintf(stderr, "%s\n", message);
-                       fflush(stderr);
-                       break;
-               }
-       
-               set_terminal_color(stderr, COLOR_DEFAULT);
-               if(oldOutputCP != CP_UTF8)
-               {
-                       SetConsoleOutputCP(oldOutputCP);
-               }
+       case QtCriticalMsg:
+       case QtFatalMsg:
+               set_terminal_color(stderr, COLOR_RED);
+               fprintf(stderr, GURU_MEDITATION);
+               fprintf(stderr, "%s\n", message);
+               break;
+       case QtWarningMsg:
+               set_terminal_color(stderr, COLOR_YELLOW);
+               fprintf(stderr, "%s\n", message);
+               break;
+       default:
+               set_terminal_color(stderr, COLOR_WHITE);
+               fprintf(stderr, "%s\n", message);
+               break;
        }
+
+       fflush(stderr);
 }
 
 void MUtils::Terminal::write(const int &type, const char *const message)
@@ -318,16 +383,16 @@ void MUtils::Terminal::write(const int &type, const char *const message)
 
        if(g_terminal_attached)
        {
-               write_terminal_helper(type, message);
+               write_to_terminal(type, message);
        }
        else
        {
-               write_debugger_helper(type, message);
+               write_to_debugger(type, message);
        }
 
-       if(!g_log_file.isNull())
+       if(!g_terminal_log_file.isNull())
        {
-               write_logfile_helper(g_log_file.data(), type, message);
+               write_to_logfile(g_terminal_log_file.data(), type, message);
        }
 }
 
@@ -341,17 +406,13 @@ void MUtils::Terminal::set_icon(const QIcon &icon)
 
        if(g_terminal_attached && (!(icon.isNull() || MUtils::OS::running_on_wine())))
        {
-               QLibrary kernel32("kernel32.dll");
-               if(kernel32.load())
+               typedef DWORD(__stdcall *SetConsoleIconFun)(HICON);
+               if(const SetConsoleIconFun setConsoleIconFun = MUtils::Win32Utils::resolve<SetConsoleIconFun>(QLatin1String("kernel32"), QLatin1String("SetConsoleIcon")))
                {
-                       typedef DWORD (__stdcall *SetConsoleIconFun)(HICON);
-                       if(SetConsoleIconFun SetConsoleIconPtr = (SetConsoleIconFun) kernel32.resolve("SetConsoleIcon"))
+                       if(HICON hIcon = (HICON) MUtils::Win32Utils::qicon_to_hicon(icon, 16, 16))
                        {
-                               if(HICON hIcon = qicon_to_hicon(icon, 16, 16))
-                               {
-                                       SetConsoleIconPtr(hIcon);
-                                       DestroyIcon(hIcon);
-                               }
+                               setConsoleIconFun(hIcon);
+                               DestroyIcon(hIcon);
                        }
                }
        }