From a4b92a30578815d97f67c8bffea9a570f175bacc Mon Sep 17 00:00:00 2001 From: fghzxm Date: Wed, 18 Oct 2023 13:45:17 +0800 Subject: [PATCH] Fix Unicode build This patch makes a number of code changes to make Unicode build actually work (instead of crashing). This involves using the `_t`-family of CRT string routines and using the `_T()` macro to wrap char and string literals wherever Unicode needs to be handled. Some macros are also added to util.h that perform explicit zero-extending operations on character types; this is to defend against build setups whose compiler (possibly through a command line switch) treats `char` and/or `wchar_t` as signed, therefore performs sign-extension when a `char` is implicitly converted to an `int` or a `wchar_t`. Care has been taken to ensure Unicode build of the program doesn't do things differently from the ASCII/MBCS build; in particular config file operations remain ASCII-based as they are today. --- ntop.c | 66 +++++++++++++++++++++++++++++----------------------------- util.h | 17 +++++++++++++++ vi.c | 42 ++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 54 deletions(-) diff --git a/ntop.c b/ntop.c index 7abde76..9a69983 100644 --- a/ntop.c +++ b/ntop.c @@ -69,7 +69,7 @@ static int ConPrintf(TCHAR *Fmt, ...) return CharsWritten; } -static void ConPutc(char c) +static void ConPutc(TCHAR c) { DWORD Dummy; WriteConsole(ConsoleHandle, &c, 1, &Dummy, 0); @@ -924,13 +924,13 @@ static input_mode InputMode = EXEC; * fact that a Windows system cannot possibly reach an uptime of over 100 days * without crashing. */ -static void FormatTimeString(TCHAR *Buffer, ULONGLONG MS) +static void FormatTimeString(TCHAR *Buffer, DWORD BufferSize, ULONGLONG MS) { int Seconds = (int)(MS / 1000 % 60); int Minutes = (int)(MS / 60000 % 60); int Hours = (int)(MS / 3600000 % 24); int Days = (int)(MS / 86400000); - wsprintf(Buffer, _T("%02d:%02d:%02d:%02d"), Days, Hours, Minutes, Seconds); + _stprintf_s(Buffer, BufferSize, _T("%02d:%02d:%02d:%02d"), Days, Hours, Minutes, Seconds); } static void FormatMemoryString(TCHAR *Buffer, DWORD BufferSize, unsigned __int64 Memory) @@ -952,12 +952,12 @@ static void FormatMemoryString(TCHAR *Buffer, DWORD BufferSize, unsigned __int64 _tcscpy_s(Unit, _countof(Unit), _T("TB")); } - sprintf_s(Buffer, BufferSize, _T("% 8.1f %s"), Value, Unit); + _stprintf_s(Buffer, BufferSize, _T("% 8.1f %s"), Value, Unit); } static void WriteBlankLine(void) { - ConPrintf(_T("%*c"), Width, ' '); + ConPrintf(_T("%*c"), Width, _T(' ')); } static BOOL WINAPI CtrlHandler(DWORD signal) @@ -984,7 +984,7 @@ static void DrawProcessListHeader(const process_list_column *Columns, int Count) } for(; CharsWritten < Width; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } } @@ -1004,7 +1004,7 @@ static void DrawOptions(const options_column *Columns, int Count) } for(; CharsWritten < Width; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } } @@ -1017,24 +1017,24 @@ static int DrawPercentageBar(TCHAR *Name, double Percentage, WORD Color) SetColor(Config.FGHighlightColor); CharsWritten += ConPrintf(_T(" %s"), Name); SetColor(Config.FGColor); - ConPutc('['); + ConPutc(_T('[')); CharsWritten++; int Bars = (int)((double)BAR_WIDTH * Percentage); SetColor(Color); for(int i = 0; i < Bars; i++) { - ConPutc('|'); + ConPutc(_T('|')); } CharsWritten+= Bars; SetColor(Config.FGColor); for(int i = 0; i < BAR_WIDTH - Bars; i++) { - ConPutc(' '); + ConPutc(_T(' ')); } CharsWritten += BAR_WIDTH - Bars; SetColor(Config.BGColor); CharsWritten += ConPrintf(_T("%04.1f%%"), 100.0 * Percentage); SetColor(Config.FGColor); - ConPutc(']'); + ConPutc(_T(']')); CharsWritten++; return CharsWritten; } @@ -1061,7 +1061,7 @@ static void WriteProcessInfo(const process *Process, BOOL Highlighted) int CharsWritten = 0; TCHAR UpTimeStr[TIME_STR_SIZE]; - FormatTimeString(UpTimeStr, Process->UpTime); + FormatTimeString(UpTimeStr, TIME_STR_SIZE, Process->UpTime); TCHAR MemoryStr[256]; FormatMemoryString(MemoryStr, _countof(MemoryStr), Process->UsedMemory); @@ -1110,7 +1110,7 @@ static void WriteProcessInfo(const process *Process, BOOL Highlighted) } - ConPrintf(_T("%*c"), Width-CharsWritten+1, ' '); + ConPrintf(_T("%*c"), Width-CharsWritten+1, _T(' ')); } static ULONGLONG KeyPressStart = 0; @@ -1236,7 +1236,7 @@ static void PrintHelpEntries(const TCHAR *Name, int Count, const help_entry *Ent ConPrintf(_T("\t%s\n"), Entry.Explanation); } - ConPutc('\n'); + ConPutc(_T('\n')); } static void PrintHelp(const TCHAR *argv0) @@ -1373,11 +1373,11 @@ static void WriteVi(void) int CharsWritten = ConPrintf(_T("\n%s"), ViMessage); for (; CharsWritten < Width + 1; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } } else { SetColor(FOREGROUND_WHITE); - ConPutc('\n'); + ConPutc(_T('\n')); WriteBlankLine(); } } @@ -1521,12 +1521,12 @@ static void ProcessInput(BOOL *Redraw) *Redraw = TRUE; break; case '/': - ViEnableInput('/'); + ViEnableInput(_T('/')); ResetCaret(); *Redraw = TRUE; break; case ':': - ViEnableInput(':'); + ViEnableInput(_T(':')); ResetCaret(); *Redraw = TRUE; break; @@ -1586,13 +1586,13 @@ int _tmain(int argc, TCHAR *argv[]) for(int i = 1; i < argc; i++) { if(argv[i][0] == _T('-') && _tcslen(argv[i]) == 2) { switch(argv[i][1]) { - case 'C': + case _T('C'): Monochrome = TRUE; break; - case 'h': + case _T('h'): PrintHelp(argv[0]); return EXIT_SUCCESS; - case 's': + case _T('s'): if(++i < argc) { if(!GetProcessSortTypeFromName(argv[i], &ProcessSortType)) { ConPrintf(_T("Unknown column: '%s'\n"), argv[i]); @@ -1600,16 +1600,16 @@ int _tmain(int argc, TCHAR *argv[]) } } break; - case 'u': + case _T('u'): if(++i < argc) { FilterByUserName = TRUE; _tcscpy_s(FilterUserName, UNLEN, argv[i]); } break; - case 'v': + case _T('v'): PrintVersion(); return EXIT_SUCCESS; - case 'p': + case _T('p'): if(++i < argc) { const TCHAR *Delim = _T(","); TCHAR *Context; @@ -1689,13 +1689,13 @@ int _tmain(int argc, TCHAR *argv[]) int MenuBarOffsetX = Width / 2 - (int)_tcslen(MenuBar) / 2; for(int i = 0; i < MenuBarOffsetX; i++) { - ConPutc(' '); + ConPutc(_T(' ')); } ConPrintf(_T("%s"), MenuBar); for(int i = 0; i < Width - MenuBarOffsetX - (int)_tcslen(MenuBar); i++) { - ConPutc(' '); + ConPutc(_T(' ')); } SetColor(Config.FGColor); @@ -1737,7 +1737,7 @@ int _tmain(int argc, TCHAR *argv[]) CharsWritten += TaskInfoChars; for(; CharsWritten < Width; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } /* Memory */ @@ -1749,7 +1749,7 @@ int _tmain(int argc, TCHAR *argv[]) CharsWritten += ConPrintf(_T("%d GB"), (int)TotalMemory/1000); for(; CharsWritten < Width; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } CharsWritten = DrawPercentageBar(_T("Pge"), UsedPageMemoryPerc, Config.PageMemoryBarColor); @@ -1758,13 +1758,13 @@ int _tmain(int argc, TCHAR *argv[]) CharsWritten += ConPrintf(_T(" Uptime: ")); TCHAR Buffer[TIME_STR_SIZE]; - FormatTimeString(Buffer, UpTime); + FormatTimeString(Buffer, TIME_STR_SIZE, UpTime); SetColor(Config.FGColor); CharsWritten += ConPrintf(_T("%s"), Buffer); SetColor(Config.FGColor); for(; CharsWritten < Width; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } WriteBlankLine(); @@ -1816,17 +1816,17 @@ int _tmain(int argc, TCHAR *argv[]) if (InInputMode) { CharsWritten = ConPrintf(_T("\n%s"), CurrentInputStr); if (CaretState) { - ConPutc('_'); + ConPutc(_T('_')); ++CharsWritten; } for(; CharsWritten < Width; CharsWritten++) { - ConPutc(' '); + ConPutc(_T(' ')); } } else if(ViMessageActive()) { WriteVi(); } else { - ConPrintf(_T("\n%*c"), Width - 1, ' '); + ConPrintf(_T("\n%*c"), Width - 1, _T(' ')); } SetConsoleMode(ConsoleHandle, ENABLE_PROCESSED_INPUT|ENABLE_WRAP_AT_EOL_OUTPUT); diff --git a/util.h b/util.h index 2af4f21..1a1fff5 100644 --- a/util.h +++ b/util.h @@ -33,4 +33,21 @@ void *xmalloc(size_t size); void *xrealloc(void *ptr, size_t size); void *xcalloc(size_t num, size_t size); +#ifdef UNICODE + /* + * Zero extension is correct for converting any legal ASCII char + * to its corresponding UTF-16LE code unit + */ + #define TCharFromAscii(c) ((TCHAR)(unsigned char)c) +#else + #define TCharFromAscii(c) (c) +#endif + +/* + * Use these zero-extending functions to defend against stdlib undefined behavior + * specified by the is*-family of ctype functions + */ +#define IntFromTChar(t) ((int)(unsigned int)t) +#define IntFromChar(c) ((int)(unsigned int)c) + #endif diff --git a/vi.c b/vi.c index 78f2e77..33af5b1 100644 --- a/vi.c +++ b/vi.c @@ -130,7 +130,7 @@ COMMAND_FUNC(exec) if(i != Argc - 1) { DWORD Length = (DWORD)_tcslen(CommandLine); - CommandLine[Length] = L' '; + CommandLine[Length] = _T(' '); } } @@ -231,7 +231,7 @@ static void FreeCmdParseResult(cmd_parse_result *ParseResult) static TCHAR *EatSpaces(TCHAR *Str) { - while(_istspace(*Str)) { + while(_istspace(IntFromTChar(*Str))) { Str++; } return Str; @@ -239,7 +239,7 @@ static TCHAR *EatSpaces(TCHAR *Str) static BOOL IsValidCharacter(TCHAR c) { - return (_istalnum(c) || c == _T('%')) || c == '/' || c == '.'; + return (_istalnum(IntFromTChar(c)) || c == _T('%')) || c == _T('/') || c == _T('.'); } static BOOL ParseCommand(TCHAR *Str, cmd_parse_result *Result) @@ -256,17 +256,17 @@ static BOOL ParseCommand(TCHAR *Str, cmd_parse_result *Result) /* * TODO: should allow for commands to contain digits but not start with them */ - while(_istalpha(*Str)) { + while(_istalpha(IntFromTChar(*Str))) { Result->Name[i++] = *Str++; } - Result->Name[i] = '\0'; + Result->Name[i] = _T('\0'); /* * Do error when we stopped on an non-alphanumeric character and not on a * terminator or space */ - if(*Str != _T('\0') && !_istspace(*Str)) { + if(*Str != _T('\0') && !_istspace(IntFromTChar(*Str))) { /* parse error */ free(Result->Name); return FALSE; @@ -274,16 +274,16 @@ static BOOL ParseCommand(TCHAR *Str, cmd_parse_result *Result) int InQuotes = FALSE; - if(*Str != '\0') { + if(*Str != _T('\0')) { i = 0; /* * Read arguments */ - while(*Str != '\0') { + while(*Str != _T('\0')) { Str = EatSpaces(Str); - if(*Str == '\0') { + if(*Str == _T('\0')) { break; } @@ -301,16 +301,16 @@ static BOOL ParseCommand(TCHAR *Str, cmd_parse_result *Result) * until we hit a closing quote or terminator */ do { - while(_istspace(*Str) || IsValidCharacter(*Str)) { + while(_istspace(IntFromTChar(*Str)) || IsValidCharacter(*Str)) { Result->Args[i][j++] = *Str++; } - if(*Str == '\"') { + if(*Str == _T('\"')) { ++Str; InQuotes = FALSE; break; } - } while(*Str != '\0'); + } while(*Str != _T('\0')); } else { /* * When not inside quotes then only read single token @@ -320,14 +320,14 @@ static BOOL ParseCommand(TCHAR *Str, cmd_parse_result *Result) } } - Result->Args[i][j] = '\0'; + Result->Args[i][j] = _T('\0'); ++i; /* * Do error and clean up if either the string wasn't closed * or if we hit a non-alphanumeric character or non space */ - if(InQuotes || (*Str != _T('\0') && !_istspace(*Str))) { + if(InQuotes || (*Str != _T('\0') && !_istspace(IntFromTChar(*Str)))) { /* parse error */ FreeCmdParseResult(Result); return FALSE; @@ -343,7 +343,7 @@ static void TryExec(TCHAR *Str) cmd_parse_result ParseResult; /* Search has an alias */ - if(Str[0] == '/') { + if(Str[0] == _T('/')) { TCHAR *Args[1]; Args[0] = Str + 1; search_func(1, Args); @@ -371,7 +371,7 @@ static void TryExec(TCHAR *Str) void ViInit(void) { - CurrentInputStr = xcalloc(DEFAULT_STR_SIZE, 1); + CurrentInputStr = xcalloc(DEFAULT_STR_SIZE, sizeof *CurrentInputStr); } void ViEnableInput(TCHAR InitialKey) @@ -409,7 +409,7 @@ int ViHandleInputKey(KEY_EVENT_RECORD *KeyEvent) break; case VK_BACK: if(InputIndex != 0) { - CurrentInputStr[--InputIndex] = '\0'; + CurrentInputStr[--InputIndex] = _T('\0'); if(InputIndex == 0) { ViDisableInput(); } @@ -423,8 +423,8 @@ int ViHandleInputKey(KEY_EVENT_RECORD *KeyEvent) ViExecInput(); return 1; default: - if(isprint(KeyEvent->uChar.AsciiChar)) { - CurrentInputStr[InputIndex++] = KeyEvent->uChar.AsciiChar; + if(isprint(IntFromChar(KeyEvent->uChar.AsciiChar))) { + CurrentInputStr[InputIndex++] = TCharFromAscii(KeyEvent->uChar.AsciiChar); return 1; } } @@ -439,14 +439,14 @@ void ViExecInput(void) /* * Ignore all preceeding colons and spaces */ - while(*Cmd == ':' || _istspace(*Cmd)) { + while(*Cmd == _T(':') || _istspace(IntFromTChar(*Cmd))) { Cmd++; } /* * We check for empty string here because empty strings shouldn't throw errors */ - if(*Cmd != '\0') { + if(*Cmd != _T('\0')) { TryExec(Cmd); PushToHistory(CurrentInputStr); }