From dd65db75ae4e77a345827c6f3f2ed703815354b6 Mon Sep 17 00:00:00 2001 From: zmatsuo <6488847+zmatsuo@users.noreply.github.com> Date: Tue, 30 Jun 2026 22:59:39 +0900 Subject: [PATCH] =?UTF-8?q?dumb=E7=AB=AF=E6=9C=AB=E3=81=A7=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E5=8F=96=E5=BE=97=E6=99=82=E3=80=81=E3=82=BF=E3=82=A4?= =?UTF-8?q?=E3=83=A0=E3=82=B9=E3=82=BF=E3=83=B3=E3=83=97=E3=82=92vt=20wind?= =?UTF-8?q?ow=E3=81=AB=E3=82=82=E5=87=BA=E5=8A=9B=20#255?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dumb端末時のみ有効 - dumb以外(VT100等)では、従来通りログファイルのみに出力 - ログ設定でタイムスタンプをenableにすると - VT windowの行頭に出力 - 実際にログをとっている場合、ログファイルにも出力 - TERATERM.INI の LogTimestampFormat を Unicode化した - ttlib.h に ttstrftime() を追加 - 現在時刻をstrftime風format指定で、文字列を作成 - mctimelocal() を Unicode 化して置き換え --- doc/en/html/about/history.html | 5 + doc/en/html/menu/setup-additional-log.html | 6 +- .../html/menu/setup-additional-terminal.html | 3 +- doc/ja/html/about/history.html | 5 + doc/ja/html/menu/setup-additional-log.html | 6 +- .../html/menu/setup-additional-terminal.html | 3 +- teraterm/common/ttlib.c | 132 ++---------- teraterm/common/ttlib.h | 2 + teraterm/common/ttlib_static.c | 190 ++++++++++++++++++ teraterm/common/tttypes.h | 3 +- teraterm/teraterm/filesys_log.cpp | 63 ++++-- teraterm/teraterm/filesys_log.h | 2 + teraterm/teraterm/vtterm.c | 13 ++ teraterm/ttpcmn/ttcmn_dup.cpp | 1 + teraterm/ttpset/ttset.c | 9 +- ttssh2/ttxssh/ttxssh.c | 6 +- 16 files changed, 296 insertions(+), 153 deletions(-) diff --git a/doc/en/html/about/history.html b/doc/en/html/about/history.html index 931f54bdf..86c8a6192 100644 --- a/doc/en/html/about/history.html +++ b/doc/en/html/about/history.html @@ -85,6 +85,11 @@

YYYY.MM.DD (Ver 5.7.0 not released yet)

Changed DPI awareness setting to process-wide instead of GUI thread only. (issue #1314) +
  • + When Terminal ID is "dumb", + Output timestamps to both the VT window and log file when logging. + (issue #255) +
  • diff --git a/doc/en/html/menu/setup-additional-log.html b/doc/en/html/menu/setup-additional-log.html index 110ec7820..0a4cb21fe 100644 --- a/doc/en/html/menu/setup-additional-log.html +++ b/doc/en/html/menu/setup-additional-log.html @@ -141,7 +141,11 @@

    Additional settings / "Log" Tab ([Setup]
    Timestamp
    - If you select this option, a timestamp is added on top of each log line. The timestamp is added when the first character of the line appears.
    + If you select this option, timestamp is output when first character of the line appears.
    + When Terminal ID is dumb, + timestamp is output to both VTWin and log file.
    + When it is other than dumb, the timestamp is output to log file.
    +
    The timestamp type can be selected from following.
    Local Time
    diff --git a/doc/en/html/menu/setup-additional-terminal.html b/doc/en/html/menu/setup-additional-terminal.html index abb1a0c01..acab432c6 100644 --- a/doc/en/html/menu/setup-additional-terminal.html +++ b/doc/en/html/menu/setup-additional-terminal.html @@ -77,7 +77,8 @@

    Additional settings / "Terminal" tab ([S

    dumb
    - Emulates dumb terminal. + Emulates dumb terminal.
    + log Timestamp is output to VT window and log file.
    diff --git a/doc/ja/html/about/history.html b/doc/ja/html/about/history.html index d7e85e1cf..3489c32ed 100644 --- a/doc/ja/html/about/history.html +++ b/doc/ja/html/about/history.html @@ -85,6 +85,11 @@

    YYYY.MM.DD (Ver 5.7.0 not released yet)

    DPIݒGUIXbĥ݂ł͂ȂAvZXŜōs悤ɂB (issue #1314) +
  • + Terminal ID "dumb" ̎A + O擾̃^CX^vVT WindowƃOt@C֏o͂悤ɂB + (issue #255) +
  • diff --git a/doc/ja/html/menu/setup-additional-log.html b/doc/ja/html/menu/setup-additional-log.html index dfe647c43..b3bb7a4c8 100644 --- a/doc/ja/html/menu/setup-additional-log.html +++ b/doc/ja/html/menu/setup-additional-log.html @@ -137,7 +137,11 @@
    Timestamp
    - O̍sɎljꍇ͑IĂBs̍ŏ̕o͂ꂽlj܂B
    + IAs̍ŏ̕o͂ꂽɁAo͂܂B
    + Terminal IDdumb̎A + VTWinƃOt@Cɏo͂܂B
    + dumbȊO̎A̓Ot@Cɏo͂܂B
    +
    ^CX^v̎ʂƂĈȉIł܂B
    Local Time
    diff --git a/doc/ja/html/menu/setup-additional-terminal.html b/doc/ja/html/menu/setup-additional-terminal.html index 1bc80a17d..42f769d7a 100644 --- a/doc/ja/html/menu/setup-additional-terminal.html +++ b/doc/ja/html/menu/setup-additional-terminal.html @@ -77,7 +77,8 @@
    dumb
    - _[G~[g܂B + _[G~[g܂B
    + OTimestampVT windowɏo͂܂
    diff --git a/teraterm/common/ttlib.c b/teraterm/common/ttlib.c index 12d865a08..5961857fc 100644 --- a/teraterm/common/ttlib.c +++ b/teraterm/common/ttlib.c @@ -844,110 +844,22 @@ DWORD get_OPENFILENAME_SIZEW() return CDSIZEOF_STRUCT(OPENFILENAMEW,lpTemplateName); } -/* +/** * 現在の時間を文字列にして返す * + * @param format strftime likeなフォーマット + * @param utc_flag TRUE / FALSE = UTCで出力 / ローカルタイムで出力 * @return 経過時刻文字列 * 不要になったらfree()すること */ char *mctimelocal(const char *format, BOOL utc_flag) { - SYSTEMTIME systime; - const size_t sizeof_strtime = 29; - char *strtime = malloc(sizeof_strtime); - static const char week[][4] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char month[][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - char tmp[5]; - unsigned int i = strlen(format); - - *strtime = '\0'; - if (utc_flag) { - GetSystemTime(&systime); - } - else { - GetLocalTime(&systime); - } - for (i=0; imicro->milli + time_t now = now_ms / 1000; + + struct tm tm; + if (utc_flag) { + // UTC + gmtime_s(&tm, &now); + } else { + // local + localtime_s(&tm, &now); + } + + // 独自フォーマットで展開する + // 未対応の指定子はそのまま出力する + size_t format_len = wcslen(format); + + // 出力バッファ + // 独自フォーマット展開時の最大展開率は %Y(2文字->4文字) 等で 2 倍程度。 + // 余裕を持って format 長の 4 倍 + α を確保する。 + // (それでも溢れる場合は wcsncat_s が切り詰めるためオーバーフローしない) + size_t sizeof_strtime = format_len * 4 + 1; + wchar_t *strtime = malloc(sizeof(wchar_t) * sizeof_strtime); + if (strtime == NULL) { + return NULL; + } + + static const wchar_t week[][4] = { + L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" + }; + static const wchar_t month[][4] = { + L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", + L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" + }; + size_t i; + + strtime[0] = L'\0'; + for (i = 0; i < format_len; i++) { + if (format[i] == L'%') { + wchar_t tmp[5]; + wchar_t c = format[i + 1]; + switch (c) { + case 'a': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%s", week[tm.tm_wday]); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'A' 未実装 + case 'b': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%s", month[tm.tm_mon]); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'c' 未実装 + case 'd': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%02d", tm.tm_mday); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + case 'e': + // マニュアルに記載なし + // %Hの0埋めなし("01"ではなく"1"など)、 + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%2d", tm.tm_mday); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + case 'H': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%02d", tm.tm_hour); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'I' 未実装 + case 'N': + // Tera Term オリジナル実装 + // ミリ秒 3桁 + // マニュアルに記載なし + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%03d", (int)(now_ms % 1000)); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + case 'm': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%02d", tm.tm_mon + 1); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + case 'M': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%02d", tm.tm_min); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'p' 未実装 + case 'S': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%02d", tm.tm_sec); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'U' 未実装 + case 'w': + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%d", tm.tm_wday); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'W' 未実装 + // 'x' 未実装 + // 'X' 未実装 + case 'y': + // 年 2桁 + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%02d", (tm.tm_year + 1900) % 100); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + case 'Y': + // 年 4桁 + _snwprintf_s(tmp, _countof(tmp), _TRUNCATE, L"%04d", (tm.tm_year + 1900)); + wcsncat_s(strtime, sizeof_strtime, tmp, _TRUNCATE); + i++; + break; + // 'z' 未実装 + // 'Z' 未実装 + case '%': + wcsncat_s(strtime, sizeof_strtime, L"%", _TRUNCATE); + i++; + break; + case '\0': + default: + // 未対応の指定子は '%' を残してそのまま出力する + // (i は進めないので、次ループで指定子文字がリテラルとして連結される) + wcsncat_s(strtime, sizeof_strtime, L"%", _TRUNCATE); + break; + } + } + else { + const wchar_t lit[2] = { format[i], L'\0' }; + wcsncat_s(strtime, sizeof_strtime, lit, _TRUNCATE); + } + } + + return strtime; +} + +/* + * 現在までの経過時間を文字列にして返す + * + * @return 経過時間の文字列 + * 不要になったらfree()すること + */ +wchar_t *strelapsedW(DWORD start_time) +{ + size_t sizeof_strtime = 20; + wchar_t *strtime = malloc(sizeof(wchar_t) * sizeof_strtime); + int days, hours, minutes, seconds, msecs; + DWORD delta = GetTickCount() - start_time; + + msecs = delta % 1000; + delta /= 1000; + + seconds = delta % 60; + delta /= 60; + + minutes = delta % 60; + delta /= 60; + + hours = delta % 24; + days = delta / 24; + + _snwprintf_s(strtime, sizeof_strtime, _TRUNCATE, + L"%d %02d:%02d:%02d.%03d", + days, hours, minutes, seconds, msecs); + + return strtime; +} + /* vim: set ts=4 sw=4 ff=dos : */ diff --git a/teraterm/common/tttypes.h b/teraterm/common/tttypes.h index 523ad1908..1121f95ba 100644 --- a/teraterm/common/tttypes.h +++ b/teraterm/common/tttypes.h @@ -556,7 +556,7 @@ struct tttset { int ZmodemTimeOutFin; WORD WaitCom; WORD reserve_ListHiddenFonts; - char LogTimestampFormat[48]; + char reserve_LogTimestampFormat[48]; int TerminalInputSpeed; int TerminalOutputSpeed; char reserve_DialogFontName[LF_FACESIZE]; // DialogFontNameW へ移行 @@ -616,6 +616,7 @@ struct tttset { BYTE UnicodeOverrideCharWidthEnable; // 0/1=overrideしない/する(文字幅Override設定存在時) BYTE UnicodeOverrideCharWidthSelected; // 0... int BroadcastSubmitKey; + wchar_t *LogTimestampFormatW; // Experimental BYTE ExperimentalTreePropertySheetEnable; diff --git a/teraterm/teraterm/filesys_log.cpp b/teraterm/teraterm/filesys_log.cpp index 26eb3ddf6..413dcceec 100644 --- a/teraterm/teraterm/filesys_log.cpp +++ b/teraterm/teraterm/filesys_log.cpp @@ -572,33 +572,52 @@ static void LogRotate(PFileVar fv) logfile_unlock(fv); } -static wchar_t *TimeStampStr(PFileVar fv) +/** + * タイムスタンプ用時間文字列を作成する + * + * @param timestamp_type + * TIMESTAMP_LOCAL + * TIMESTAMP_UTC + * TIMESTAMP_ELAPSED_LOGSTART + * TIMESTAMP_ELAPSED_CONNECTED + * @param format LOCAL or UTC 時のフォーマット + * @param start_time LOGSTART の開始時間 + * @param connected_time CONNECTED の開始時間 + * @return 時間文字列、不要になったらfree()すること + */ +wchar_t* FLogTimeStampStrW(enum LogTimestampType timestamp_type, const wchar_t* format, + DWORD start_time, DWORD connected_time) { - char *strtime = NULL; - switch (ts.LogTimestampType) { - case TIMESTAMP_LOCAL: + wchar_t *time_strW = NULL; + switch (timestamp_type) { default: - strtime = mctimelocal(ts.LogTimestampFormat, FALSE); + assert(FALSE); + // FALLTHROUGH + case TIMESTAMP_LOCAL: + time_strW = ttstrftime(format, FALSE); break; case TIMESTAMP_UTC: - strtime = mctimelocal(ts.LogTimestampFormat, TRUE); + time_strW = ttstrftime(format, TRUE); break; case TIMESTAMP_ELAPSED_LOGSTART: - strtime = strelapsed(fv->StartTime); + time_strW = strelapsedW(start_time); break; case TIMESTAMP_ELAPSED_CONNECTED: - strtime = strelapsed(cv.ConnectedTime); + time_strW = strelapsedW(connected_time); break; } - char tmp[128]; - tmp[0] = 0; - strncat_s(tmp, sizeof(tmp), "[", _TRUNCATE); - strncat_s(tmp, sizeof(tmp), strtime, _TRUNCATE); - strncat_s(tmp, sizeof(tmp), "] ", _TRUNCATE); - free(strtime); + wchar_t *ret; + aswprintf(&ret, L"[%s] ", time_strW); + free(time_strW); - return ToWcharA(tmp); + return ret; +} + +static wchar_t *TimeStampStr(PFileVar fv) +{ + return FLogTimeStampStrW((enum LogTimestampType)ts.LogTimestampType, + ts.LogTimestampFormatW, fv->StartTime, cv.ConnectedTime); } /** @@ -1082,12 +1101,14 @@ static void FLogPutUTF32_(PFileVar fv, unsigned int u32) } // 行頭か?(改行を出力した直後) - if (ts.LogTimestamp && fv->eLineEnd) { - // タイムスタンプを出力 - fv->eLineEnd = Line_Other; /* clear endmark*/ - wchar_t* strtime = TimeStampStr(fv); - FLogWriteStr(strtime); - free(strtime); + if (ts.TerminalID != IdDUMB && ts.LogTimestamp) { + if (fv->eLineEnd) { + // タイムスタンプを出力 + fv->eLineEnd = Line_Other; /* clear endmark*/ + wchar_t* strtime = TimeStampStr(fv); + FLogWriteStr(strtime); + free(strtime); + } } switch(fv->log_code) { diff --git a/teraterm/teraterm/filesys_log.h b/teraterm/teraterm/filesys_log.h index 5b74fb422..e7b059aad 100644 --- a/teraterm/teraterm/filesys_log.h +++ b/teraterm/teraterm/filesys_log.h @@ -74,6 +74,8 @@ void FLogPutBinary(BYTE b); void FLogBinSkip(int add); void FLogOutputAllBuffer(void); +wchar_t* FLogTimeStampStrW(enum LogTimestampType timestamp_type, const wchar_t* format, + DWORD start_time, DWORD connected_time); #ifdef __cplusplus } diff --git a/teraterm/teraterm/vtterm.c b/teraterm/teraterm/vtterm.c index bb1a8722e..0d3217359 100644 --- a/teraterm/teraterm/vtterm.c +++ b/teraterm/teraterm/vtterm.c @@ -5408,6 +5408,19 @@ int VTParse() printf("%02x(%c) ", b, isprint(b) ? b : '.'); } #endif + if (ts.TerminalID == IdDUMB && ts.LogTimestamp) { + if (CursorX == 0) { + if (b != '\n' && b != '\r') { + wchar_t *time_strW = FLogTimeStampStrW(ts.LogTimestampType, + ts.LogTimestampFormatW, cv.ConnectedTime, + cv.ConnectedTime); + for (size_t i = 0; i < wcslen(time_strW); i++) { + PutU32(time_strW[i]); + } + free(time_strW); + } + } + } switch (ParseMode) { case ModeFirst: ParseFirst(charset_data, b); diff --git a/teraterm/ttpcmn/ttcmn_dup.cpp b/teraterm/ttpcmn/ttcmn_dup.cpp index d2161c9ce..d3841db9b 100644 --- a/teraterm/ttpcmn/ttcmn_dup.cpp +++ b/teraterm/ttpcmn/ttcmn_dup.cpp @@ -257,6 +257,7 @@ static const TSerializeInfo serialize_info[] = { { MALLOCED_WSTRING_INFO(TTTSet, DelimListW) }, { MALLOCED_WSTRING_INFO(TTTSet, ViewlogEditorW) }, { MALLOCED_WSTRING_INFO(TTTSet, ViewlogEditorArg) }, + { MALLOCED_WSTRING_INFO(TTTSet, LogTimestampFormatW) }, { 0, 0, TSerializeInfo::COPY }, }; diff --git a/teraterm/ttpset/ttset.c b/teraterm/ttpset/ttset.c index 6ac6c1631..03d794ecf 100644 --- a/teraterm/ttpset/ttset.c +++ b/teraterm/ttpset/ttset.c @@ -993,9 +993,8 @@ void PASCAL _ReadIniFile(const wchar_t *FName, PTTSet ts) ts->LogAllBuffIncludedInFirst = GetOnOff(Section, "LogIncludeScreenBuffer", FName, FALSE); /* Timestamp format of Log each line */ - GetPrivateProfileString(Section, "LogTimestampFormat", "%Y-%m-%d %H:%M:%S.%N", - ts->LogTimestampFormat, sizeof(ts->LogTimestampFormat), - FName); + hGetPrivateProfileStringW(SectionW, L"LogTimestampFormat", L"%Y-%m-%d %H:%M:%S.%N", FName, + &ts->LogTimestampFormatW); /* Timestamp type */ GetPrivateProfileString(Section, "LogTimestampType", "", Temp, sizeof(Temp), FName); @@ -2542,8 +2541,8 @@ void PASCAL _WriteIniFile(const wchar_t *FName, PTTSet ts) WriteOnOff(Section, "LogIncludeScreenBuffer", FName, ts->LogAllBuffIncludedInFirst); /* Timestamp format of Log each line */ - WritePrivateProfileString(Section, "LogTimestampFormat", - ts->LogTimestampFormat, FName); + WritePrivateProfileStringW(SectionW, L"LogTimestampFormat", + ts->LogTimestampFormatW, FName); /* Timestamp type */ switch (ts->LogTimestampType) { diff --git a/ttssh2/ttxssh/ttxssh.c b/ttssh2/ttxssh/ttxssh.c index b2f1421ee..dcce29d7c 100644 --- a/ttssh2/ttxssh/ttxssh.c +++ b/ttssh2/ttxssh/ttxssh.c @@ -830,14 +830,14 @@ void logputs(int level, char *msg) { if (level <= pvar->settings.LogLevel) { char *buf; - char *strtime; + wchar_t *strtime; int len; int file; BOOL enable_log = TRUE; BOOL enable_outputdebugstring = FALSE; - strtime = mctimelocal("%Y-%m-%d %H:%M:%S.%NZ", TRUE); - len = asprintf(&buf, "%s [%lu] %s\n", + strtime = ttstrftime(L"%Y-%m-%d %H:%M:%S.%NZ", TRUE); + len = asprintf(&buf, "%ls [%lu] %s\n", strtime, GetCurrentProcessId(), msg); free(strtime);