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 @@
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);