From 800ca0c4f150de75c38c87a59d18878b0341f16b Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Fri, 22 Jul 2016 15:47:52 +0100 Subject: [PATCH] Added UTF-8 functions. We want to use UTF-8 everywhere. To that end we use new functions that will convert strings between UTF-8 and UTF-16. Before doing anything at all we set the console, if we have one, to codepage UTF-8. Before exiting we restore the console codepage to whatever it was before. --- console.cpp | 9 +++-- console.h | 2 +- nssm.cpp | 58 +++++++++++++--------------- nssm.h | 2 + nssm.vcproj | 16 ++++++-- service.cpp | 2 +- utf8.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ utf8.h | 13 +++++++ 8 files changed, 187 insertions(+), 41 deletions(-) create mode 100644 utf8.cpp create mode 100644 utf8.h diff --git a/console.cpp b/console.cpp index 9a97729..b886021 100644 --- a/console.cpp +++ b/console.cpp @@ -1,23 +1,24 @@ #include "nssm.h" /* See if we were launched from a console window. */ -void check_console() { +bool check_console() { /* If we're running in a service context there will be no console window. */ HWND console = GetConsoleWindow(); - if (! console) return; + if (! console) return false; unsigned long pid; - if (! GetWindowThreadProcessId(console, &pid)) return; + if (! GetWindowThreadProcessId(console, &pid)) return false; /* If the process associated with the console window handle is the same as this process, we were not launched from an existing console. The user probably double-clicked our executable. */ - if (GetCurrentProcessId() != pid) return; + if (GetCurrentProcessId() != pid) return true; /* We close our new console so that subsequent messages appear in a popup. */ FreeConsole(); + return false; } /* Helpers for drawing the banner. */ diff --git a/console.h b/console.h index cbccd3c..7c3ce4a 100644 --- a/console.h +++ b/console.h @@ -1,7 +1,7 @@ #ifndef CONSOLE_H #define CONSOLE_H -void check_console(); +bool check_console(); void alloc_console(nssm_service_t *); #endif diff --git a/nssm.cpp b/nssm.cpp index ca79a02..9fde560 100644 --- a/nssm.cpp +++ b/nssm.cpp @@ -8,6 +8,11 @@ static TCHAR unquoted_imagepath[PATH_LENGTH]; static TCHAR imagepath[PATH_LENGTH]; static TCHAR imageargv0[PATH_LENGTH]; +void nssm_exit(int status) { + unsetup_utf8(); + exit(status); +} + /* Are two strings case-insensitively equivalent? */ int str_equiv(const TCHAR *a, const TCHAR *b) { size_t len = _tcslen(a); @@ -219,22 +224,13 @@ const TCHAR *nssm_exe() { } int _tmain(int argc, TCHAR **argv) { - check_console(); - -#ifdef UNICODE - /* - Ensure we write in UTF-16 mode, so that non-ASCII characters don't get - mangled. If we were compiled in ANSI mode it won't work. - */ - _setmode(_fileno(stdout), _O_U16TEXT); - _setmode(_fileno(stderr), _O_U16TEXT); -#endif + if (check_console()) setup_utf8(); /* Remember if we are admin */ check_admin(); /* Set up function pointers. */ - if (get_imports()) exit(111); + if (get_imports()) nssm_exit(111); /* Remember our path for later. */ _sntprintf_s(imageargv0, _countof(imageargv0), _TRUNCATE, _T("%s"), argv[0]); @@ -249,34 +245,34 @@ int _tmain(int argc, TCHAR **argv) { Valid commands are: start, stop, pause, continue, install, edit, get, set, reset, unset, remove */ - if (str_equiv(argv[1], _T("start"))) exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2)); - if (str_equiv(argv[1], _T("stop"))) exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("start"))) nssm_exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("stop"))) nssm_exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("restart"))) { int ret = control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2); - if (ret) exit(ret); - exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2)); + if (ret) nssm_exit(ret); + nssm_exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2)); } - if (str_equiv(argv[1], _T("pause"))) exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2)); - if (str_equiv(argv[1], _T("continue"))) exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2)); - if (str_equiv(argv[1], _T("status"))) exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2)); - if (str_equiv(argv[1], _T("rotate"))) exit(control_service(NSSM_SERVICE_CONTROL_ROTATE, argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("pause"))) nssm_exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("continue"))) nssm_exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("status"))) nssm_exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("rotate"))) nssm_exit(control_service(NSSM_SERVICE_CONTROL_ROTATE, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("install"))) { - if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL)); + if (! is_admin) nssm_exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL)); create_messages(); - exit(pre_install_service(argc - 2, argv + 2)); + nssm_exit(pre_install_service(argc - 2, argv + 2)); } if (str_equiv(argv[1], _T("edit")) || str_equiv(argv[1], _T("get")) || str_equiv(argv[1], _T("set")) || str_equiv(argv[1], _T("reset")) || str_equiv(argv[1], _T("unset")) || str_equiv(argv[1], _T("dump"))) { int ret = pre_edit_service(argc - 1, argv + 1); - if (ret == 3 && ! is_admin && argc == 3) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT)); + if (ret == 3 && ! is_admin && argc == 3) nssm_exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT)); /* There might be a password here. */ for (int i = 0; i < argc; i++) SecureZeroMemory(argv[i], _tcslen(argv[i]) * sizeof(TCHAR)); - exit(ret); + nssm_exit(ret); } - if (str_equiv(argv[1], _T("list"))) exit(list_nssm_services(argc - 2, argv + 2)); - if (str_equiv(argv[1], _T("processes"))) exit(service_process_tree(argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("list"))) nssm_exit(list_nssm_services(argc - 2, argv + 2)); + if (str_equiv(argv[1], _T("processes"))) nssm_exit(service_process_tree(argc - 2, argv + 2)); if (str_equiv(argv[1], _T("remove"))) { - if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE)); - exit(pre_remove_service(argc - 2, argv + 2)); + if (! is_admin) nssm_exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE)); + nssm_exit(pre_remove_service(argc - 2, argv + 2)); } } @@ -302,14 +298,14 @@ int _tmain(int argc, TCHAR **argv) { if (! StartServiceCtrlDispatcher(table)) { unsigned long error = GetLastError(); /* User probably ran nssm with no argument */ - if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) exit(usage(1)); + if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) nssm_exit(usage(1)); log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DISPATCHER_FAILED, error_string(error), 0); free_imports(); - exit(100); + nssm_exit(100); } } - else exit(usage(1)); + else nssm_exit(usage(1)); /* And nothing more to do */ - exit(0); + nssm_exit(0); } diff --git a/nssm.h b/nssm.h index 5709bd6..214d1d8 100644 --- a/nssm.h +++ b/nssm.h @@ -46,6 +46,7 @@ #include #include #include +#include "utf8.h" #include "service.h" #include "account.h" #include "console.h" @@ -61,6 +62,7 @@ #include "gui.h" #endif +void nssm_exit(int); int str_equiv(const TCHAR *, const TCHAR *); int quote(const TCHAR *, TCHAR *, size_t); void strip_basename(TCHAR *); diff --git a/nssm.vcproj b/nssm.vcproj index d5b924e..3cd1132 100755 --- a/nssm.vcproj +++ b/nssm.vcproj @@ -646,6 +646,10 @@ RelativePath="settings.cpp" > + + + + @@ -768,7 +776,7 @@ @@ -778,7 +786,7 @@ @@ -789,7 +797,7 @@ diff --git a/service.cpp b/service.cpp index 20ebcd9..2d20ff3 100644 --- a/service.cpp +++ b/service.cpp @@ -2078,7 +2078,7 @@ void CALLBACK end_service(void *arg, unsigned char why) { stop_service(service, exitcode, false, default_action); wait_for_hooks(service, false); free_imports(); - exit(exitcode); + nssm_exit(exitcode); } } diff --git a/utf8.cpp b/utf8.cpp new file mode 100644 index 0000000..1265008 --- /dev/null +++ b/utf8.cpp @@ -0,0 +1,126 @@ +#include "nssm.h" + +static unsigned long cp; + +void setup_utf8() { +#ifdef UNICODE + /* + Ensure we write in UTF-8 mode, so that non-ASCII characters don't get + mangled. If we were compiled in ANSI mode it won't work. + */ + cp = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + _setmode(_fileno(stdout), _O_U8TEXT); + _setmode(_fileno(stderr), _O_U8TEXT); +#endif +} + +void unsetup_utf8() { + if (cp) SetConsoleOutputCP(cp); +} + +/* + Conversion functions. + + to_utf8/16() converts a string which may be either utf8 or utf16 to + the desired format. If no conversion is needed a new string is still + allocated and the old string is copied. + + from_utf8/16() converts a string which IS in the specified format to + whichever format is needed according to whether UNICODE is defined. + It simply wraps the appropriate to_utf8/16() function. + + Therefore the caller must ALWAYS free the destination pointer after a + successful (return code 0) call to one of these functions. + + The length pointer is optional. Pass NULL if you don't care about + the length of the converted string. + + Both the destination and the length, if supplied, will be zeroed if + no conversion was done. +*/ +int to_utf8(const wchar_t *utf16, char **utf8, unsigned long *utf8len) { + *utf8 = 0; + if (utf8len) *utf8len = 0; + int size = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL); + if (! size) return 1; + + *utf8 = (char *) HeapAlloc(GetProcessHeap(), 0, size); + if (! *utf8) return 2; + + if (! WideCharToMultiByte(CP_UTF8, 0, utf16, -1, (LPSTR) utf8, size, NULL, NULL)) { + HeapFree(GetProcessHeap(), 0, *utf8); + *utf8 = 0; + return 3; + } + + if (utf8len) *utf8len = (unsigned long) strlen(*utf8); + + return 0; +} + +int to_utf8(const char *ansi, char **utf8, unsigned long *utf8len) { + *utf8 = 0; + if (utf8len) *utf8len = 0; + size_t len = strlen(ansi); + int size = (int) len + 1; + + *utf8 = (char *) HeapAlloc(GetProcessHeap(), 0, size); + if (! *utf8) return 2; + + if (utf8len) *utf8len = (unsigned long) len; + memmove(*utf8, ansi, size); + + return 0; +} + +int to_utf16(const char *utf8, wchar_t **utf16, unsigned long *utf16len) { + *utf16 = 0; + if (utf16len) *utf16len = 0; + int size = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); + if (! size) return 1; + + *utf16 = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, size * sizeof(wchar_t)); + if (! *utf16) return 2; + + if (! MultiByteToWideChar(CP_UTF8, 0, utf8, -1, *utf16, size)) { + HeapFree(GetProcessHeap(), 0, *utf16); + *utf16 = 0; + return 3; + } + + if (utf16len) *utf16len = (unsigned long) wcslen(*utf16); + + return 0; +} + +int to_utf16(const wchar_t *unicode, wchar_t **utf16, unsigned long *utf16len) { + *utf16 = 0; + if (utf16len) *utf16len = 0; + size_t len = wcslen(unicode); + int size = ((int) len + 1) * sizeof(wchar_t); + + *utf16 = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, size); + if (! *utf16) return 2; + + if (utf16len) *utf16len = (unsigned long) len; + memmove(*utf16, unicode, size); + + return 0; +} + +int from_utf8(const char *utf8, TCHAR **buffer, unsigned long *buflen) { +#ifdef UNICODE + return to_utf16(utf8, buffer, buflen); +#else + return to_utf8(utf8, buffer, buflen); +#endif +} + +int from_utf16(const wchar_t *utf16, TCHAR **buffer, unsigned long *buflen) { +#ifdef UNICODE + return to_utf16(utf16, buffer, buflen); +#else + return to_utf8(utf16, buffer, buflen); +#endif +} diff --git a/utf8.h b/utf8.h new file mode 100644 index 0000000..ccc535a --- /dev/null +++ b/utf8.h @@ -0,0 +1,13 @@ +#ifndef UTF8_H +#define UTF8_H + +void setup_utf8(); +void unsetup_utf8(); +int to_utf8(const wchar_t *, char **, unsigned long *); +int to_utf8(const char *, char **, unsigned long *); +int to_utf16(const char *, wchar_t **utf16, unsigned long *); +int to_utf16(const wchar_t *, wchar_t **utf16, unsigned long *); +int from_utf8(const char *, TCHAR **, unsigned long *); +int from_utf16(const wchar_t *, TCHAR **, unsigned long *); + +#endif -- 2.7.4