[ttssh2-commit] [9725] cygterm Unicode化

Back to archive index
scmno****@osdn***** scmno****@osdn*****
2022年 2月 6日 (日) 02:46:18 JST


Revision: 9725
          https://osdn.net/projects/ttssh2/scm/svn/commits/9725
Author:   zmatsuo
Date:     2022-02-06 02:46:17 +0900 (Sun, 06 Feb 2022)
Log Message:
-----------
cygterm Unicode化

- Unicodeフォルダのttermpro.exeを起動できるようになった
- 設定に関する部分を cygterm_cfg.cc,h に分離した
  - cygterm.cfgの読み書きを集める
- teraterm/common/ の関数を sub.cpp へコピー
  - cyterm を単体でビルドすることを考慮するため
- cmakeビルド時に tar.gz を生成するようにした
- cygterm.rc は生成されるので svn から削除
- ポータブル版仮対応
  - 無効化

Modified Paths:
--------------
    trunk/.editorconfig
    trunk/cygwin/cygterm/CMakeLists.txt
    trunk/cygwin/cygterm/Makefile
    trunk/cygwin/cygterm/README-j
    trunk/cygwin/cygterm/cygterm.cc
    trunk/doc/ja/html/setup/folder.md

Added Paths:
-----------
    trunk/cygwin/cygterm/cygterm_cfg.cc
    trunk/cygwin/cygterm/cygterm_cfg.h
    trunk/cygwin/cygterm/sub.cpp
    trunk/cygwin/cygterm/sub.h

Removed Paths:
-------------
    trunk/cygwin/cygterm/cygterm.rc

-------------- next part --------------
Modified: trunk/.editorconfig
===================================================================
--- trunk/.editorconfig	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/.editorconfig	2022-02-05 17:46:17 UTC (rev 9725)
@@ -4,7 +4,7 @@
 indent_style = tab
 indent_size = 4
 
-[*.{cpp,c,h}]
+[*.{cpp,c,h,cc}]
 indent_style = tab
 indent_size = 4
 end_of_line = crlf

Modified: trunk/cygwin/cygterm/CMakeLists.txt
===================================================================
--- trunk/cygwin/cygterm/CMakeLists.txt	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/cygwin/cygterm/CMakeLists.txt	2022-02-05 17:46:17 UTC (rev 9725)
@@ -1,8 +1,11 @@
 cmake_minimum_required(VERSION 3.11)
 
+option(UNICODE "use Unicode Win32 API" ON)
+
 message("CMAKE_COMMAND=${CMAKE_COMMAND}")
 message("CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}")
 message("CMAKE_HOST_SYSTEM_NAME=${CMAKE_HOST_SYSTEM_NAME}")
+message("UNICODE=${UNICODE}")
 if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "MSYS")
   message("MSYS2TERM=ON")
   set(MSYS2TERM ON)
@@ -17,10 +20,16 @@
 project(${PACKAGE_NAME})
 ENABLE_LANGUAGE(RC)
 
+file(WRITE "${CMAKE_CURRENT_LIST_DIR}/cygterm.rc" "icon ICON cygterm.ico")
+
 add_executable(
   ${PACKAGE_NAME}
   cygterm.cc
-  cygterm.rc
+  cygterm_cfg.cc
+  cygterm_cfg.h
+  sub.cpp
+  sub.h
+  ${CMAKE_CURRENT_LIST_DIR}/cygterm.rc
   )
 
 if (MSYS2TERM)
@@ -42,8 +51,18 @@
   PRIVATE
   -D_GNU_SOURCE
   -fno-exceptions
+  -Wall -Wextra
   )
 
+if(UNICODE)
+  target_compile_options(
+    ${PACKAGE_NAME}
+    PRIVATE
+    -DUNICODE=1
+    -D_UNICODE=1
+    )
+endif()
+
 target_link_options(
   ${PACKAGE_NAME}
   PRIVATE
@@ -50,6 +69,13 @@
   -mwindows
   )
 
+target_link_libraries(
+  ${PACKAGE_NAME}
+  PRIVATE
+  shell32
+  ole32
+  )
+
 install(
   TARGETS ${PACKAGE_NAME}
   DESTINATION .
@@ -58,3 +84,31 @@
   FILES ${PACKAGE_NAME}.cfg
   DESTINATION .
   )
+
+
+set(ARCHIVE "cygterm+.tar.gz")
+
+set(SRC
+  cygterm.cc
+  cygterm_cfg.cc
+  cygterm_cfg.h
+  sub.cpp
+  sub.h
+  #
+  cygterm.ico
+  #
+  cygterm.cfg
+  msys2term.cfg
+  )
+
+add_custom_target(
+  tar ALL
+  SOURCES ${CMAKE_CURRENT_LIST_DIR}/${ARCHIVE}
+  )
+
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_LIST_DIR}/${ARCHIVE}
+  DEPENDS ${SRC}
+  COMMAND ${CMAKE_COMMAND} -E tar cvz ${CMAKE_CURRENT_LIST_DIR}/${ARCHIVE} COPYING README README-j Makefile CMakeLists.txt ${SRC}
+  WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+  )

Modified: trunk/cygwin/cygterm/Makefile
===================================================================
--- trunk/cygwin/cygterm/Makefile	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/cygwin/cygterm/Makefile	2022-02-05 17:46:17 UTC (rev 9725)
@@ -3,12 +3,18 @@
 BINDIR = $(HOME)/bin
 
 CC = gcc
-CFLAGS = -D_GNU_SOURCE -O2 -fno-exceptions
-#CFLAGS = -g -fno-exceptions
+CFLAGS = -D_GNU_SOURCE -O2 -fno-exceptions -DUNICODE -D_UNICODE
+#CFLAGS = -D_GNU_SOURCE -O2 -fno-exceptions
+CXXFLAGS = $(CFLAGS)
 LDFLAGS = -mwindows
 
 EXE = cygterm.exe
-SRC = $(EXE:.exe=.cc)
+SRC = \
+	$(EXE:.exe=.cc) \
+	cygterm_cfg.cc \
+	cygterm_cfg.h \
+	sub.cpp \
+	sub.h
 CFG = $(EXE:.exe=.cfg)
 RES = $(EXE:.exe=.res)
 ICO = $(EXE:.exe=.ico)
@@ -19,20 +25,18 @@
 
 all : $(EXE) $(ARCHIVE)
 
-$(EXE) : $(SRC) $(ICO) $(RC)
-	windres -O coff -o $(RES) $(RC)
-  ifeq (0, $(shell nm /usr/lib/crt0.o | grep -c WinMainCRTStartup))
-	$(CC) $(CFLAGS) $(LDFLAGS) -DNO_WIN_MAIN -o $(EXE) $(SRC) $(RES)
-  else
-	$(CC) $(CFLAGS) $(LDFLAGS) -o $(EXE) $(SRC) $(RES)
-  endif
+$(EXE) : cygterm.o cygterm_cfg.o sub.o $(RES)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $(EXE) $^ -lole32
 	strip $(EXE)
 
+$(RES): $(RC)
+	windres -O coff -o $(RES) $(RC)
+
 $(RC):
 	echo 'icon ICON $(ICO)' > $(RC)
 
 clean :
-	rm -f $(EXE) $(RC) $(RES) $(ARCHIVE)
+	rm -f $(EXE) $(RC) $(RES) $(ARCHIVE) *.o *.obj
 
 install : $(EXE)
 	@ install -v $(EXE) $(BINDIR)/$(EXE)
@@ -44,5 +48,17 @@
 	rm -f $(BINDIR)/$(EXE)
 	rm -f $(BINDIR)/$(CFG)
 
-$(ARCHIVE) : $(SRC) $(ICO) $(CFG) README README-j Makefile
-	tar cf - $(SRC) $(ICO) $(CFG) COPYING README README-j Makefile | gzip > $(ARCHIVE)
+$(ARCHIVE) : $(SRC) $(ICO) $(CFG) README README-j Makefile CMakeLists.txt
+	tar cf - $(SRC) $(ICO) $(CFG) COPYING README README-j Makefile CMakeLists.txt msys2term.cfg | gzip > $(ARCHIVE)
+
+cygterm.o:
+  ifeq (0, $(shell nm /usr/lib/crt0.o | grep -c WinMainCRTStartup))
+	$(CXX) $(CXXFLAGS) -DNO_WIN_MAIN cygterm.cc -c -o $@
+  else
+	$(CXX) $(CXXFLAGS) cygterm.cc -c -o $@
+  endif
+
+# cc -M *.cc *.cpp
+cygterm.o: cygterm.cc sub.h cygterm_cfg.h
+cygterm_cfg.o: cygterm_cfg.cc cygterm_cfg.h
+sub.o: sub.cpp sub.h

Modified: trunk/cygwin/cygterm/README-j
===================================================================
--- trunk/cygwin/cygterm/README-j	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/cygwin/cygterm/README-j	2022-02-05 17:46:17 UTC (rev 9725)
@@ -91,18 +91,31 @@
        BINDIR $B$K(B cygterm.exe $B$H(B cygterm.cfg $B$,%$%s%9%H!<%k$5$l$^$9!#(B
        cygterm.cfg $B$O>e=q$-$5$l$^$;$s!#(B
 
+// $BF0:n%b!<%I(B //
+
+    cygterm.exe $B$HF1$8%U%)%k%@$K(B portable.ini $B$,$"$k>l9g%]!<%?%V%kHG$H(B
+    $B$7$FF0:n$7$^$9!#(Bportable.ini $B$,$J$$>l9g$ODL>oHG$H$7$FF0:n$7$^$9!#(B
+
 // $B @ _(B $BDj(B $B%U(B $B%!(B $B%$(B $B%k(B //
 
     cygterm.cfg $B$O @ _Dj%U%!%$%k$G$9!#(B $B;HMQ$9$kC<Kv%(%_%e%l!<%?$N%3%^%s%I%i%$%s(B
     $B$d5/F0$9$k%7%'%k$N%3%^%s%I%i%$%sEy$r @ _Dj$7$^$9!#(B
 
-    cygterm.cfg $B$O<!$N=g=x$GFI$_9~$^$l$^$9!#(B
-      - $B%3%^%s%I%i%$%s0z?t(B
-      - exe $B$HF1$8%U%)%k%@(B
-      - /etc/cygterm.conf (cygwin$B$N(B/etc/)
-      - $APPDATA/teraterm5/cygterm.cfg
-      - ~/.cygtermrc
+    cygterm.cfg $B$O<!$N=g=x$GFI$_$^$9!#(B
 
+    - exe$B$HF1$8%U%)%k%@$N(B cygterm.cfg
+    - /etc/cygterm.conf
+    - $APPDATA/teraterm5/cygterm.cfg
+    - ~/.cygtermrc
+
+    $SHELL $B$H(B $USER $B$O!"@_Dj%U%!%$%kFI$_9~$_A0$K @ _Dj$5$l$^$9!#(B
+    $B8e$+$iFI$_9~$s$@CM$G>e=q$-$5$l$^$9!#(B
+    $B @ _Dj%U%!%$%k$rFI$_9~$s$@$N$A%3%^%s%I%i%$%s%*%W%7%g%s$G;XDj$G>e=q$-$5$l$^$9!#(B
+
+    $B%]!<%?%V%kHG$N$H$-$O!"<!$N%U%!%$%k$N$_$rFI$_9~$_$^$9!#(B
+
+    - $APPDATA/teraterm5/cygterm.cfg
+
     cygterm.cfg $B$NNc(B
       +-----------------------------------------------------------------------
       | TERM = C:\program files\ttermpro\ttermpro.exe %s %d /KR=SJIS /KT=SJIS
@@ -110,10 +123,10 @@
       | PORT_START = 20000
       | PORT_RANGE = 40
       | SHELL = /bin/bash
-      | ENV_1 = MAKE_MODE=unix
-      | ENV_2 = HOME=/home
-      |   :         :
 
+    $B @ _Dj%U%!%$%k$NJ8;z%3!<%I$O(BUTF-8$B$G$9!#(B
+    (Cygwin 1.5$B4D6-$G$O(B Shift_JIS $B$G$9!#(B)
+
     TERM
     ----
         $B%?!<%_%J%k!&%(%_%e%l!<%?$N5/F0%3%^%s%I%i%$%s$G$9!#(B
@@ -268,9 +281,11 @@
     TCP/IP Port# $B$K(B 23 $B$r;XDj$7$F @ _DjJ]B8$7D>$;$P85$KLa$j$^$9!#(B
 
 // $BJQ(B $B99(B $BMz(B $BNr(B //
-v1.07_30(beta) 2021/11/14
+v1.07_30 2022/02/06
         * $BFI$_9~$`@_Dj%U%!%$%k$rDI2C(B
           $APPDATA/teraterm5/cygterm.cfg
+        * Unicode$BBP1~(B
+        * $B%]!<%?%V%kHGBP1~(B($B2>(B)
 
 v1.07_29 2016/11/26 (by maya)
         * $B%"%$%3%s$rJQ99$7$?!#(B

Modified: trunk/cygwin/cygterm/cygterm.cc
===================================================================
--- trunk/cygwin/cygterm/cygterm.cc	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/cygwin/cygterm/cygterm.cc	2022-02-05 17:46:17 UTC (rev 9725)
@@ -1,1539 +1,1434 @@
-/////////////////////////////////////////////////////////////////////////////
-// CygTerm+ - yet another Cygwin console
-// Copyright (C) 2000-2006 NSym.
-// (C) 2006-2016 TeraTerm Project
-//---------------------------------------------------------------------------
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License (GPL) as published by
-// the Free Software Foundation; either version 2 of the License, or (at
-// your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-//---------------------------------------------------------------------------
-
-/////////////////////////////////////////////////////////////////////////////
-// CygTerm+ - yet another Cygwin console
-//
-//   Using Cygwin with a terminal emulator.
-//   
-//   Writtern by TeraTerm Project.
-//            https://ttssh2.osdn.jp/
-//   
-//   Original written by NSym.
-//                         *** Web Pages ***
-//  (English) http://www.dd.iij4u.or.jp/~nsym/cygwin/cygterm/index-e.html
-// (Japanese) http://www.dd.iij4u.or.jp/~nsym/cygwin/cygterm/index.html
-//
-
-static char Program[] = "CygTerm+";
-static char Version[] = "version 1.07_30_beta (2021/11/14)";
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/wait.h>
-#include <arpa/inet.h>
-#include <windows.h>
-#include <shlobj.h>
-#include <pwd.h>
-#include <sys/select.h>
-#include <wchar.h>
-
-// pageant support (ssh-agent proxy)
-//----------------------------------
-#define AGENT_COPYDATA_ID 0x804e50ba
-#define AGENT_MAX_MSGLEN 8192
-char sockdir[] = "/tmp/ssh-XXXXXXXXXX";
-char sockname[256];
-
-// PTY device name
-//----------------
-#define  DEVPTY  "/dev/ptmx"
-
-// TCP port for TELNET
-//--------------------
-int port_start = 20000;  // default lowest port number
-int port_range = 40;     // default number of ports
-
-// command lines of a terminal-emulator and a shell
-//-------------------------------------------------
-char cmd_term[256] = "";
-char cmd_termopt[256] = "";
-char cmd_shell[128] = "";
-char pw_shell[128] = "";
-char change_dir[256] = "";
-
-// TCP port for connection to another terminal application
-//--------------------------------------------------------
-int cl_port = 0;
-
-// telnet socket timeout
-//----------------------
-int telsock_timeout = 5;    // timeout 5 sec
-
-// dumb terminal flag
-//-------------------
-bool dumb = false;
-
-// chdir to HOME
-//--------------
-bool home_chdir = false;
-
-// login shell flag
-//-----------------
-bool enable_loginshell = false;
-
-// ssh agent proxy
-//----------------
-bool enable_agent_proxy = false;
-
-// terminal type & size
-//---------------------
-char term_type[41] = "";
-struct winsize win_size = {0,0,0,0};
-
-// debug mode
-//-----------
-bool debug_flag = false;
-
-// additional env vars given to a shell
-//-------------------------------------
-struct sh_env_t {
-    struct sh_env_t* next;
-    char env[1];
-} sh_env = {NULL, ""};
-
-sh_env_t* sh_envp = &sh_env;
-
-int add_env(sh_env_t** envp, const char* str, const char* str2)
-{
-	int len;
-	sh_env_t* e;
-
-	len = strlen(str);
-	if (str2) {
-		len += strlen(str2) + 1;
-	}
-
-	e = (sh_env_t*)malloc(sizeof(sh_env_t) + len);
-	if (e) {
-		if (str2) {
-			snprintf(e->env, len + 1, "%s=%s", str, str2);
-		}
-		else {
-			strcpy(e->env, str);
-		}
-		e->next = NULL;
-		*envp = ((*envp)->next = e);
-		return 1;
-	}
-	else {
-		return 0;
-	}
-}
-
-//================//
-// message output //
-//----------------//
-void msg_print(const char* msg)
-{
-    MessageBox(NULL, msg, Program, MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
-}
-
-//=========================//
-// Win32-API error message //
-//-------------------------//
-void api_error(const char* string = NULL)
-{
-    char msg[1024];
-    char *ptr = msg;
-    if (string != NULL)
-        ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string);
-    FormatMessage(
-        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-        NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-        ptr, sizeof(msg)-(ptr-msg), NULL
-    );
-    msg_print(msg);
-}
-
-//=========================//
-// C-runtime error message //
-//-------------------------//
-void c_error(const char* string = NULL)
-{
-    char msg[1024];
-    char *ptr = msg;
-    if (string != NULL)
-        ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string);
-    snprintf(ptr, sizeof(msg)-(ptr-msg), "%s\n", strerror(errno));
-    msg_print(msg);
-}
-
-//======================//
-// debug message output //
-//======================//
-void debug_msg_print(const char* msg)
-{
-    if (debug_flag) {
-        msg_print(msg);
-    }
-}
-
-//==================================//
-// parse line in configuration file //
-//----------------------------------//
-void parse_cfg_line(char *buf)
-{
-    // "KEY = VALUE" format in each line.
-    // skip leading/trailing blanks. KEY is not case-sensitive.
-    char* p1;
-    for (p1 = buf; isspace(*p1); ++p1);
-    if (!isalpha(*p1)) {
-        return; // comment line with non-alphabet 1st char
-    }
-    char* name = p1;
-    for (++p1; isalnum(*p1) || *p1 == '_'; ++p1);
-    char* p2;
-    for (p2 = p1; isspace(*p2); ++p2);
-    if (*p2 != '=') {
-        return; // igonore line without '='
-    }
-    for (++p2; isspace(*p2); ++p2);
-    char* val = p2;
-    for (p2 += strlen(p2); isspace(*(p2-1)); --p2);
-    *p1 = *p2 = 0;
-
-    if (!strcasecmp(name, "TERM")) {
-        // terminal emulator command line (host:%s, port#:%d)
-        strncpy(cmd_term, val, sizeof(cmd_term)-1);
-        cmd_term[sizeof(cmd_term)-1] = 0;
-    }
-    else if (!strcasecmp(name, "SHELL")) {
-        // shell command line
-        if (strcasecmp(val, "AUTO") != 0) {
-            strncpy(cmd_shell, val, sizeof(cmd_shell)-1);
-        }
-	else {
-	    strncpy(cmd_shell, pw_shell, sizeof(cmd_shell)-1);
-	}
-	cmd_shell[sizeof(cmd_shell)-1] = 0;
-    }
-    else if (!strcasecmp(name, "PORT_START")) {
-        // minimum port# for TELNET
-        port_start = atoi(val);
-    }
-    else if (!strcasecmp(name, "PORT_RANGE")) {
-        // number of ports for TELNET
-        port_range = atoi(val);
-    }
-    else if (!strcasecmp(name, "TERM_TYPE")) {
-        // terminal type name (maybe overridden by TELNET negotiation.)
-        strncpy(term_type, val, sizeof(term_type)-1);
-        term_type[sizeof(term_type)-1] = 0;
-    }
-    else if (!strncasecmp(name, "ENV_", 4)) {
-        // additional env vars given to a shell
-	add_env(&sh_envp, val, NULL);
-    }
-    else if (!strcasecmp(name, "HOME_CHDIR")) {
-        // change directory to home
-        if (strchr("YyTt", *val) != NULL || atoi(val) > 0) {
-            home_chdir = true;
-        }
-    }
-    else if (!strcasecmp(name, "LOGIN_SHELL")) {
-        // execute a shell as a login shell
-        if (strchr("YyTt", *val) != NULL || atoi(val) > 0) {
-            enable_loginshell = true;
-        }
-    }
-    else if (!strcasecmp(name, "SOCKET_TIMEOUT")) {
-        // telnet socket timeout
-        telsock_timeout = atoi(val);
-    }
-    else if (!strcasecmp(name, "SSH_AGENT_PROXY")) {
-        // ssh-agent proxy
-        if (strchr("YyTt", *val) != NULL || atoi(val) > 0) {
-            enable_agent_proxy = true;
-        }
-    }
-    else if (!strcasecmp(name, "DEBUG")) {
-        // debug mode
-        if (strchr("YyTt", *val) != NULL || atoi(val) > 0) {
-            debug_flag = true;
-        }
-    }
-
-    return;
-}
-
-// '\\' -> '/'
-void convert_bs(char *path)
-{
-    char *p = path;
-    while(*p != 0) {
-        if (*p == '\\') {
-            *p = '/';
-        }
-        p++;
-    }
-}
-
-// L'\\' -> L'/'
-void convert_bsW(wchar_t *path)
-{
-    wchar_t *p = path;
-    while(*p != 0) {
-        if (*p == L'\\') {
-            *p = L'/';
-        }
-        p++;
-    }
-}
-
-// wchar -> utf8
-char *convert_utf8_from_wchar(const wchar_t *strW)
-{
-    size_t mb_len = ::WideCharToMultiByte(CP_UTF8, 0, strW, -1, NULL, 0, NULL, NULL);
-    char *u8 = (char *)malloc(sizeof(wchar_t) * mb_len);
-    ::WideCharToMultiByte(CP_UTF8, 0, strW, -1, u8, mb_len, NULL, NULL);
-    return u8;
-}
-
-// $APPDATA
-char *get_appdata_dir()
-{
-#if 0
-	// link error :-(
-    wchar_t *home_pathW;
-    SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &home_pathW);
-    convert_bsW(home_pathW);
-    char *home_pathU8 = convert_utf8_from_wchar(home_pathW);
-    CoTaskMemFree(home_pathW);
-    return home_pathU8;
-#endif
-#if 1
-    char *appdata = strdup(getenv("APPDATA"));
-    convert_bs(appdata);
-    return appdata;
-#endif
-}
-
-void get_cfg_filenames(char **cfg_exe_full, char **cfg_appdata_full, char **cfg)
-{
-    wchar_t win_conf[MAX_PATH];
-
-    // get cfg path from exe path
-    if (GetModuleFileNameW(NULL, win_conf, MAX_PATH) <= 0) {
-        *cfg_exe_full = NULL;
-        *cfg = NULL;
-        return;
-    }
-
-    convert_bsW(win_conf);
-
-    wchar_t* bcW = wcsrchr(win_conf, '/');
-    if (bcW != NULL) {
-        wchar_t* dot = wcsrchr(bcW, '.');
-        if (dot == NULL) {
-            wcscat(bcW, L".cfg");
-        } else {
-            wcscpy(dot, L".cfg");
-        }
-    }
-    char *u8 = convert_utf8_from_wchar(win_conf);
-    *cfg_exe_full = u8;
-
-    char *bs = strrchr(u8, '/');
-    *cfg = strdup(bs+1);
-
-    char *appdata = get_appdata_dir();
-    const char *teraterm = "/teraterm5/";
-    size_t len = strlen(appdata) + strlen(teraterm) + strlen(*cfg) + 1;
-    *cfg_appdata_full = (char *)malloc(sizeof(wchar_t) * len);
-    strcpy(*cfg_appdata_full, appdata);
-    strcat(*cfg_appdata_full, teraterm);
-    strcat(*cfg_appdata_full, *cfg);
-    free(appdata);
-}
-
-//====================//
-// load configuration //
-//--------------------//
-void load_cfg()
-{
-    // configuration file (.cfg) path
-    char *conf_exe_full;
-    char *conf_appdata_full;
-    char *conf_base;
-    get_cfg_filenames(&conf_exe_full, &conf_appdata_full, &conf_base);
-
-    char sys_conf[] = "/etc/cygterm.conf";
-
-    // user configuration file (~/.*rc) path
-    char usr_conf[MAX_PATH] = "";
-
-    // auto generated configuration file path
-    char tmp_conf[MAX_PATH] = "/tmp/cygtermrc.XXXXXX";
-
-    // get user name from getlogin().  if it fails, use $USERNAME instead.
-    // and get /etc/passwd information by getpwnam(3) with user name,
-    // and generate temporary configuration file by mktemp(3).
-    const char* username = getlogin();
-    if (username == NULL)
-        username = getenv("USERNAME");
-    if (username != NULL) {
-        struct passwd* pw_ent = getpwnam(username);
-        if (pw_ent != NULL) {
-            strncpy(pw_shell, pw_ent->pw_shell, sizeof(pw_shell)-1);
-            pw_shell[sizeof(pw_shell)-1] = 0;
-
-            strcpy(usr_conf, pw_ent->pw_dir);
-            strcat(usr_conf, "/.");
-            strcat(usr_conf, conf_base);
-            char* dot = strrchr(usr_conf, '.');
-            if (dot == NULL) {
-                strcat(usr_conf, "rc");
-            } else {
-                strcpy(dot, "rc");
-            }
-        }
-        int fd = mkstemp(tmp_conf);
-        FILE* fp = fdopen(fd, "w");
-        if (fp != NULL) {
-            if (pw_ent != NULL) {
-                fprintf(fp, "ENV_1=USER=%s\n",  pw_ent->pw_name);
-                fprintf(fp, "ENV_2=SHELL=%s\n", pw_ent->pw_shell);
-                fprintf(fp, "SHELL=%s\n", pw_ent->pw_shell);
-            } else {
-                fprintf(fp, "ENV_1=USER=%s\n",       username);
-            }
-            fclose(fp);
-        }
-    }
-
-    if (strcmp(usr_conf, "") == 0) {
-        strcpy(usr_conf, "");
-        strcpy(tmp_conf, "");
-    }
-
-    char const *conf_path[] = {
-        tmp_conf,
-        conf_exe_full,      // [exe directory]/cygterm.cfg
-        sys_conf,           // /etc/cygterm.conf
-        conf_appdata_full,  // $APPDATA/teraterm5/cygterm.cfg
-        usr_conf            // ~/cygtermrc
-    };
-    for (int i = 0; i < sizeof(conf_path)/sizeof(conf_path[0]); i++) {
-        // ignore empty configuration file path
-        if (strcmp(conf_path[i], "") == 0) {
-            continue;
-        }
-        // read each setting parameter
-        FILE* fp;
-        if ((fp = fopen(conf_path[i], "r")) == NULL) {
-            continue;
-        }
-        char buf[BUFSIZ];
-        while (fgets(buf, sizeof(buf), fp) != NULL) {
-            parse_cfg_line(buf);
-        }
-        fclose(fp);
-    }
-
-    // remove temporary configuration file, if it was generated.
-    if (strcmp(tmp_conf, "") != 0) {
-        unlink(tmp_conf);
-    }
-
-    free(conf_base);
-    free(conf_exe_full);
-    free(conf_appdata_full);
-}
-
-void quote_cut(char *dst, size_t len, char *src) {
-	while (*src && len > 1) {
-		if (*src != '"') {
-			*dst++ = *src;
-		}
-		src++;
-	}
-	*dst = 0;
-}
-
-//=======================//
-// commandline arguments //
-//-----------------------//
-void get_args(int argc, char** argv)
-{
-    char tmp[sizeof(cmd_termopt)];
-
-    for (++argv; *argv != NULL; ++argv) {
-        if (!strcmp(*argv, "-t")) {             // -t <terminal emulator>
-            if (*++argv == NULL)
-                break;
-            strncpy(cmd_term, *argv, sizeof(cmd_term)-1);
-            cmd_term[sizeof(cmd_term)-1] = '\0';
-        }
-        else if (!strcmp(*argv, "-p")) {        // -p <port#>
-            if (*(argv+1) != NULL) {
-                ++argv, cl_port = atoi(*argv);
-            }
-        }
-        else if (!strcmp(*argv, "-dumb")) {     // -dumb
-            dumb = true;
-            strcpy(term_type, "dumb");
-        }
-        else if (!strcmp(*argv, "-s")) {        // -s <shell>
-            if (*++argv == NULL)
-                break;
-	    if (strcasecmp(*argv, "AUTO") != 0) {
-		strncpy(cmd_shell, *argv, sizeof(cmd_shell)-1);
-	    }
-	    else {
-		strncpy(cmd_shell, pw_shell, sizeof(cmd_shell)-1);
-	    }
-            cmd_shell[sizeof(cmd_shell)-1] = '\0';
-        }
-        else if (!strcmp(*argv, "-cd")) {       // -cd
-            home_chdir = true;
-        }
-        else if (!strcmp(*argv, "-nocd")) {     // -nocd
-            home_chdir = false;
-        }
-        else if (!strcmp(*argv, "+cd")) {       // +cd
-            home_chdir = false;
-        }
-        else if (!strcmp(*argv, "-ls")) {       // -ls
-            enable_loginshell = true;
-        }
-        else if (!strcmp(*argv, "-nols")) {     // -nols
-            enable_loginshell = false;
-        }
-        else if (!strcmp(*argv, "+ls")) {       // +ls
-            enable_loginshell = false;
-        }
-        else if (!strcmp(*argv, "-A")) {       // -A
-            enable_agent_proxy = true;
-        }
-        else if (!strcmp(*argv, "-a")) {       // -a
-            enable_agent_proxy = false;
-        }
-        else if (!strcmp(*argv, "-v")) {        // -v <additional env var>
-            if (*(argv+1) != NULL) {
-                ++argv;
-		add_env(&sh_envp, *argv, NULL);
-            }
-        }
-        else if (!strcmp(*argv, "-d")) {        // -d <exec directory>
-            if (*++argv == NULL)
-                break;
-            quote_cut(change_dir, sizeof(change_dir), *argv);
-        }
-        else if (!strcmp(*argv, "-o")) {        // -o <additional option for terminal>
-            if (*++argv == NULL)
-                break;
-            if (cmd_termopt[0] == '\0') {
-                strncpy(cmd_termopt, *argv, sizeof(cmd_termopt)-1);
-                cmd_termopt[sizeof(cmd_termopt)-1] = '\0';
-            }
-            else {
-                snprintf(tmp, sizeof(tmp), "%s %s", cmd_termopt, *argv);
-                strncpy(cmd_termopt, tmp, sizeof(cmd_termopt)-1);
-                cmd_termopt[sizeof(cmd_termopt)-1] = '\0';
-            }
-        }
-        else if (!strcmp(*argv, "-debug")) {    // -debug
-            debug_flag = true;
-        }
-    }
-}
-
-//===================================//
-// pageant support (ssh-agent proxy) //
-//-----------------------------------//
-unsigned long get_uint32(unsigned char *buff)
-{
-	return ((unsigned long)buff[0] << 24) +
-	       ((unsigned long)buff[1] << 16) +
-	       ((unsigned long)buff[2] <<  8) +
-	       ((unsigned long)buff[3]);
-}
-
-void set_uint32(unsigned char *buff, unsigned long v)
-{
-	buff[0] = (unsigned char)(v >> 24);
-	buff[1] = (unsigned char)(v >> 16);
-	buff[2] = (unsigned char)(v >>  8);
-	buff[3] = (unsigned char)v;
-	return;
-}
-
-unsigned long agent_request(unsigned char *out, unsigned long out_size, unsigned char *in)
-{
-	HWND hwnd;
-	char mapname[25];
-	HANDLE fmap = NULL;
-	unsigned char *p = NULL;
-	COPYDATASTRUCT cds;
-	unsigned long len;
-	unsigned long ret = 0;
-
-	if (out_size < 5) {
-		return 0;
-	}
-	if ((len = get_uint32(in)) > AGENT_MAX_MSGLEN) {
-		goto agent_error;
-	}
-
-	hwnd = FindWindow("Pageant", "Pageant");
-	if (!hwnd) {
-		goto agent_error;
-	}
-
-	sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());
-	fmap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
-	                         0, AGENT_MAX_MSGLEN, mapname);
-	if (!fmap) {
-		goto agent_error;
-	}
-
-	if ((p = (unsigned char *)MapViewOfFile(fmap, FILE_MAP_WRITE, 0, 0, 0)) == NULL) {
-		goto agent_error;
-	}
-
-	cds.dwData = AGENT_COPYDATA_ID;
-	cds.cbData = strlen(mapname) + 1;
-	cds.lpData = mapname;
-
-	memcpy(p, in, len + 4);
-	if (SendMessage(hwnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds) > 0) {
-		len = get_uint32(p);
-		if (out_size >= len + 4) {
-			memcpy(out, p, len + 4);
-			ret = len + 4;
-		}
-	}
-
-agent_error:
-	if (p) {
-		UnmapViewOfFile(p);
-	}
-	if (fmap) {
-		CloseHandle(fmap);
-	}
-	if (ret == 0) {
-		set_uint32(out, 1);
-		out[4] = 5; // SSH_AGENT_FAILURE
-	}
-
-	return ret;
-}
-
-void sighandler(int sig) {
-	unlink(sockname);
-	rmdir(sockdir);
-	exit(0);
-};
-
-struct connList {
-	int sock;
-	int recvlen;
-	int sendlen;
-	struct connList *next;
-	unsigned char ibuff[AGENT_MAX_MSGLEN];
-	unsigned char obuff[AGENT_MAX_MSGLEN];
-};
-
-int proc_recvd(struct connList *conn)
-{
-	int reqlen, len;
-
-	if (conn->sendlen > 0) {
-		return 0;
-	}
-
-	if (conn->recvlen < 4) {
-		return 0;
-	}
-
-	reqlen = get_uint32(conn->ibuff) + 4;
-	if (conn->recvlen < reqlen) {
-		return 0;
-	}
-
-	len = agent_request(conn->obuff, sizeof(conn->obuff), conn->ibuff);
-
-	if (len > 0) {
-		conn->sendlen = len;
-	}
-	else {
-		set_uint32(conn->obuff, 1);
-		conn->obuff[4] = 5; // SSH_AGENT_FAILURE
-		conn->sendlen = 1;
-	}
-
-	if (conn->recvlen == reqlen) {
-		conn->recvlen = 0;
-	}
-	else {
-		conn->recvlen -= reqlen;
-		memmove(conn->ibuff, conn->ibuff + reqlen, conn->recvlen);
-	}
-
-	return 1;
-}
-
-void agent_proxy()
-{
-	int sock, asock, ret;
-	long len;
-	unsigned long reqlen;
-	struct sockaddr_un addr;
-	unsigned char tmpbuff[AGENT_MAX_MSGLEN];
-	struct connList connections, *new_conn, *prev, *cur;
-	fd_set readfds, writefds, rfds, wfds;
-	struct sigaction act;
-	sigset_t blk;
-
-	connections.next = NULL;
-
-	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
-		c_error("agent_proxy: socket failed.");
-		exit(0);
-	}
-	memset(&addr, 0, sizeof(addr));
-	addr.sun_family = AF_UNIX;
-	strlcpy(addr.sun_path, sockname, sizeof(addr.sun_path));
-
-	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		goto agent_thread_cleanup;
-	}
-	if (listen(sock, -1) < 0) {
-		goto agent_thread_cleanup;
-	}
-
-	sigfillset(&blk);
-	sigdelset(&blk, SIGKILL);
-	sigdelset(&blk, SIGSTOP);
-
-	memset(&act, 0, sizeof(act));
-	act.sa_handler = sighandler;
-	act.sa_mask = blk;
-	sigaction(SIGINT, &act, NULL);
-	sigaction(SIGTERM, &act, NULL);
-	sigaction(SIGHUP, &act, NULL);
-	sigaction(SIGQUIT, &act, NULL);
-
-	FD_ZERO(&readfds);
-	FD_ZERO(&writefds);
-	FD_SET(sock, &readfds);
-
-	while (1) {
-		memcpy(&rfds, &readfds, sizeof(fd_set));
-		memcpy(&wfds, &writefds, sizeof(fd_set));
-
-		select(FD_SETSIZE, &rfds, &wfds, NULL, NULL);
-
-		if (FD_ISSET(sock, &rfds)) {
-			asock = accept(sock, NULL, NULL);
-			if (asock < 0) {
-				if (!(errno == EINTR || errno == ECONNABORTED)) {
-					break;
-				}
-			}
-			else {
-				new_conn = (struct connList *)malloc(sizeof(struct connList));
-				if (new_conn == NULL) {
-					// no memory
-					close(sock);
-				}
-				else {
-					new_conn->sock = asock;
-					new_conn->recvlen = 0;
-					new_conn->sendlen = 0;
-					new_conn->next = connections.next;
-					connections.next = new_conn;
-					FD_SET(asock, &readfds);
-				}
-			}
-		}
-
-		prev = &connections;
-		for (cur=connections.next; cur != NULL; cur = cur->next) {
-			if (FD_ISSET(cur->sock, &wfds)) {
-				if (cur->sendlen > 0) {
-					len = send(cur->sock, cur->obuff, cur->sendlen, 0);
-					if (len < 0) {
-						// write error
-						prev->next = cur->next;
-						shutdown(cur->sock, SHUT_RDWR);
-						close(cur->sock);
-						FD_CLR(cur->sock, &writefds);
-						FD_CLR(cur->sock, &readfds);
-						free(cur);
-						cur = prev;
-						continue;
-					}
-					else if (len >= cur->sendlen) {
-						cur->sendlen = 0;
-
-						sigprocmask(SIG_BLOCK, &blk, NULL);
-						ret = proc_recvd(cur);
-						sigprocmask(SIG_UNBLOCK, &blk, NULL);
-
-						if (ret) {
-							FD_SET(cur->sock, &writefds);
-							FD_CLR(cur->sock, &readfds);
-						}
-						else {
-							FD_CLR(cur->sock, &writefds);
-							FD_SET(cur->sock, &readfds);
-						}
-					}
-					else if (len > 0) {
-						cur->sendlen -= len;
-						memmove(cur->obuff, cur->obuff+len, cur->sendlen);
-					}
-				}
-				else {
-					FD_CLR(cur->sock, &writefds);
-				}
-			}
-
-			if (FD_ISSET(cur->sock, &rfds)) {
-				len = recv(cur->sock, cur->ibuff + cur->recvlen, sizeof(cur->ibuff) - cur->recvlen, 0);
-				if (len > 0) {
-					cur->recvlen += len;
-
-					sigprocmask(SIG_BLOCK, &blk, NULL);
-					ret = proc_recvd(cur);
-					sigprocmask(SIG_UNBLOCK, &blk, NULL);
-
-					if (ret) {
-						FD_SET(cur->sock, &writefds);
-						FD_CLR(cur->sock, &readfds);
-					}
-					else {
-						FD_CLR(cur->sock, &writefds);
-						FD_SET(cur->sock, &readfds);
-					}
-				}
-				else if (len <= 0) {
-					// read error
-					prev->next = cur->next;
-					shutdown(cur->sock, SHUT_RDWR);
-					close(cur->sock);
-					FD_CLR(cur->sock, &readfds);
-					FD_CLR(cur->sock, &writefds);
-					free(cur);
-					cur = prev;
-					continue;
-				}
-			}
-		}
-	}
-
-agent_thread_cleanup:
-	shutdown(sock, SHUT_RDWR);
-	close(sock);
-
-	unlink(sockname);
-	rmdir(sockdir);
-
-	exit(0);
-}
-
-int exec_agent_proxy()
-{
-	int pid;
-	int malloc_size;
-
-	if (mkdtemp(sockdir) == NULL) {
-		return -1;
-	}
-	snprintf(sockname, sizeof(sockname), "%s/agent.%ld", sockdir, getpid());
-
-	if (!add_env(&sh_envp, "SSH_AUTH_SOCK", sockname)) {
-		return -1;
-	}
-
-	if ((pid = fork()) < 0) {
-		return -1;
-	}
-	if (pid == 0) {
-		setsid();
-		agent_proxy();
-	}
-	return pid;
-}
-
-//=============================//
-// terminal emulator execution //
-//-----------------------------//
-DWORD WINAPI term_thread(LPVOID)
-{
-    STARTUPINFO si;
-    PROCESS_INFORMATION pi;
-    FillMemory(&si, sizeof(si), 0);
-    si.cb = sizeof(si);
-    si.dwFlags = STARTF_USESHOWWINDOW;
-    si.wShowWindow = SW_SHOW;
-    DWORD flag = 0;
-    if (!CreateProcess(
-         NULL, cmd_term, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi))
-    {
-        api_error(cmd_term);
-        return 0;
-    }
-    WaitForSingleObject(pi.hProcess, INFINITE);
-    CloseHandle(pi.hProcess);
-    CloseHandle(pi.hThread);
-    return 0;
-}
-
-//============================-==========//
-// thread creation for terminal emulator //
-//---------------------------------------//
-HANDLE exec_term()
-{
-    DWORD id;
-    return CreateThread(NULL, 0, term_thread, NULL, 0, &id);
-}
-
-//=======================================//
-// listener socket for TELNET connection //
-//---------------------------------------//
-int listen_telnet(u_short* port)
-{
-    int lsock;
-    if ((lsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-        return -1;
-    }
-    struct sockaddr_in addr;
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-    int i;
-    for (i = 0; i < port_range; ++i) { // find an unused port#
-        addr.sin_port = htons(port_start + i);
-        if (bind(lsock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
-            break;
-        }
-    }
-    if (i == port_range) {
-        shutdown(lsock, 2);
-        close(lsock);
-        return -1;
-    }
-    if (listen(lsock, 1) != 0) {
-        shutdown(lsock, 2);
-        close(lsock);
-        return -1;
-    }
-    *port = addr.sin_port;
-    return lsock;
-}
-
-//=============================//
-// accept of TELNET connection //
-//-----------------------------//
-int accept_telnet(int lsock)
-{
-    fd_set rbits;
-    FD_ZERO(&rbits);
-    FD_SET(lsock, &rbits);
-    struct timeval tm;
-    tm.tv_sec = telsock_timeout;
-    tm.tv_usec = 0;
-    if (select(FD_SETSIZE, &rbits, 0, 0, &tm) <= 0) {
-        c_error("accept_telnet: select failed");
-        return -1;
-    }
-    if (!FD_ISSET(lsock, &rbits)) {
-        c_error("accept_telnet: FD_ISSET failed");
-        return -1;
-    }
-    int asock;
-    struct sockaddr_in addr;
-    int len = sizeof(addr);
-    if ((asock = accept(lsock, (struct sockaddr *)&addr, &len)) < 0) {
-        c_error("accept_telnet: accept failed");
-        return -1;
-    }
-    if (getpeername(asock, (struct sockaddr *)&addr, &len) != 0) {
-        c_error("accept_telnet: getpeername failed");
-        shutdown(asock, 2);
-        close(asock);
-        return -1;
-    }
-    if (addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
-        // reject it except local connection
-        msg_print("not local connection");
-        shutdown(asock, 2);
-        close(asock);
-        return -1;
-    }
-    return asock;
-}
-
-//============================//
-// connect to specified port# //
-//----------------------------//
-int connect_client()
-{
-    int csock;
-    if ((csock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-        return -1;
-    }
-    struct sockaddr_in addr;
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-    addr.sin_port = htons(cl_port);
-    if (connect(csock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-        close(csock);
-        return -1;
-    }
-    return csock;
-}
-
-//========================================//
-// setup *argv[] from a string for exec() //
-//----------------------------------------//
-void get_argv(char **argv, int maxc, char *s)
-{
-    int esc, sq, dq;  // recognize (\) (') (") and tokenize
-    int c, argc;
-    char *p;
-    esc = sq = dq = 0;
-    for (argc = 0; argc < maxc-1; ++argc) {
-        for ( ; isascii(*s) && isspace(*s); ++s);
-        if (*s == 0) {
-            break;
-        }
-        argv[argc] = p = s;
-        while ((c = *s) != 0) {
-            ++s;
-            if (isspace(c) && !esc && !sq && !dq) {
-                break;
-            }
-            if (c == '\'' && !esc && !dq) {
-                sq ^= 1;
-            } else if (c == '"' && !esc && !sq) {
-                dq ^= 1;
-            } else if (c == '\\' && !esc) {
-                esc = 1;
-            } else {
-                esc = 0;
-                *p++ = c;
-            }
-        }
-        *p = 0;
-    }
-    // not to judge syntax errors
-    // if (dq || sq || esc) { syntax error }
-    // if (argc == maxc) { overflow }
-    argv[argc] = NULL;
-}
-
-//=================//
-// shell execution //
-//-----------------//
-int exec_shell(int* sh_pid)
-{
-    char env_term[64];
-    // open pty master
-    int master;
-    if ((master = open(DEVPTY, O_RDWR)) < 0) {
-        c_error("exec_shell: master pty open error");
-        return -1;
-    }
-    int pid;
-    if ((pid = fork()) < 0) {
-        c_error("exec_shell: fork failed");
-        return -1;
-    }
-    if (pid == 0) {
-        // detach from control tty
-        setsid();
-        // open pty slave
-        int slave;
-        if ((slave = open(ptsname(master), O_RDWR)) < 0) {
-            c_error("exec_shell: slave pty open error");
-            exit(0);
-        }
-        // stdio redirection
-        while (slave <= 2) {
-            if ((slave = dup(slave)) < 0) {
-                exit(0);
-            }
-        }
-        int fd;
-        for (fd = 0; fd < 3; ++fd) {
-            close(fd);
-            dup(slave);
-            fcntl(fd, F_SETFD, 0);
-        }
-        for (fd = 3; fd < getdtablesize(); ++fd) {
-            if (fcntl(fd, F_GETFD) == 0) {
-                close(fd);
-            }
-        }
-        // set env vars
-        if (*term_type != 0) {
-            // set terminal type to $TERM
-            sprintf(env_term, "TERM=%s", term_type);
-            putenv(env_term);
-        }
-        // set other additional env vars
-        sh_env_t* e;
-        for (e = sh_env.next; e != NULL; e = e->next) {
-            putenv(e->env);
-        }
-	// change directory
-        if (change_dir[0] != 0) {
-	    if (chdir(change_dir) < 0) {
-		char tmp[256];
-		snprintf(tmp, 256, "exec_shell: Can't chdir to \"%s\".", change_dir);
-		tmp[255] = 0;
-		c_error(tmp);
-	    }
-        }
-        else if (home_chdir) {
-            // chdir to home directory
-            const char *home_dir = getenv("HOME");
-            // ignore chdir(2) system-call error.
-            chdir(home_dir);
-        }
-        // execute a shell
-        char *argv[32];
-        get_argv(argv, 32, cmd_shell);
-        if (enable_loginshell) {
-                char shell_path[128];
-                char *pos;
-                strcpy(shell_path, argv[0]);
-                if ((pos = strrchr(argv[0], '/')) != NULL) {
-                        *pos = '-';
-                        argv[0] = pos;
-                }
-                debug_msg_print(shell_path);
-                execv(shell_path, argv);
-        }
-        else {
-                debug_msg_print(argv[0]);
-                execv(argv[0], argv);
-        }
-        // no error, exec() doesn't return
-        c_error(argv[0]);
-        exit(0);
-    }
-    *sh_pid = pid;
-    return master;
-}
-
-//==================//
-// i/o buffer class //
-//------------------//
-class IOBuf
-{
-private:
-    int fd;
-    u_char i_buf[4096];
-    u_char o_buf[4096];
-    int i_pos, i_len, o_pos;
-public:
-    IOBuf(int channel) : fd(channel), i_pos(0), i_len(0), o_pos(0) {}
-    operator int() { return fd; }
-    void ungetc() { --i_pos; }
-    bool flush_in();
-    bool getc(u_char*);
-    bool nextc(u_char*);
-    bool putc(u_char);
-    bool flush_out();
-};
-
-// read bytes into input buffer
-//-----------------------------
-bool IOBuf::flush_in()
-{
-    if ((i_len = read(fd, i_buf, sizeof(i_buf))) <= 0)
-        return false;
-    i_pos = 0;
-    return true;
-}
-
-// get 1 char from input buffer
-//-----------------------------
-inline bool IOBuf::getc(u_char* c)
-{
-    if (i_pos == i_len) return false;
-    *c = i_buf[i_pos++];
-    return true;
-}
-
-// get next 1 char from input buffer
-//----------------------------------
-inline bool IOBuf::nextc(u_char* c)
-{
-    if (i_pos == i_len)
-        if (!flush_in()) return false;
-    *c = i_buf[i_pos++];
-    return true;
-}
-
-// put 1 char to output buffer
-//----------------------------
-inline bool IOBuf::putc(u_char c)
-{
-    if (o_pos == sizeof(o_buf))
-        if (!flush_out()) return false;
-    o_buf[o_pos++] = c;
-    return true;
-}
-
-// write bytes from output buffer
-//-------------------------------
-bool IOBuf::flush_out()
-{
-    int n;
-    for (int i = 0; i < o_pos; i += n) {
-        if ((n = write(fd, o_buf+i, o_pos-i)) <= 0) return false;
-    }
-    o_pos = 0;
-    return true;
-}
-
-//=========================//
-// TELNET command handling //  (see RFC854 TELNET PROTOCOL SPECIFICATION)
-//-------------------------//
-enum { nIAC=255, nWILL=251, nWONT=252, nDO=253, nDONT=254 };
-enum { sSEND=1, sIS=0, sSB=250, sSE=240 };
-enum { oECHO=1, oSGA=3, oTERM=24, oNAWS=31 };
-
-bool c_will_term = false;
-bool c_will_naws = false;
-
-u_char telnet_cmd(IOBuf* te)
-{
-    u_char cmd, c;
-    te->nextc(&cmd);
-    if (cmd == sSB) {
-        te->nextc(&c);
-        // accept terminal type request
-        if (c == oTERM) {                      // "SB TERM
-            te->nextc(&c);                     //     IS
-            u_char* p = (u_char*)term_type;
-            te->nextc(p);                      //     TERMINAL-TYPE
-            while (*p != nIAC) {
-                if (isupper(*p)) *p = _tolower(*p);
-                ++p; te->nextc(p);
-            }
-            *p = 0;
-            te->nextc(&c);                     //     IAC SE"
-            return (u_char)oTERM;
-        }
-        // accept terminal size request
-        if (c == oNAWS) {                      // "SB NAWS
-            u_short col, row;
-            te->nextc((u_char*)&col);
-            te->nextc((u_char*)&col+1);        //     00 00 (cols)
-            te->nextc((u_char*)&row);
-            te->nextc((u_char*)&row+1);        //     00 00 (rows)
-            te->nextc(&c);
-            te->nextc(&c);                     //     TAC SE"
-            win_size.ws_col = ntohs(col);
-            win_size.ws_row = ntohs(row);
-            return (u_char)oNAWS;
-        }
-        while (c != nIAC) te->nextc(&c);       // "... IAC SE"
-        te->nextc(&c);
-    }
-    else if (cmd == nWILL || cmd == nWONT || cmd == nDO || cmd == nDONT) {
-        u_char c;
-        te->nextc(&c);
-        if (cmd == nWILL && c == oTERM)        // "WILL TERM"
-            c_will_term = true;
-        else if (cmd == nWILL && c == oNAWS)   // "WILL NAWS"
-            c_will_naws = true;
-    }
-    return cmd;
-}
-
-//============================//
-// TELNET initial negotiation //
-//----------------------------//
-void telnet_nego(int te_sock)
-{
-    IOBuf te = te_sock;
-    u_char c;
-
-    // start terminal type negotiation
-    // IAC DO TERMINAL-TYPE
-    te.putc(nIAC); te.putc(nDO); te.putc(oTERM);
-    te.flush_out();
-    te.nextc(&c);
-    if (c != nIAC) {
-        te.ungetc();
-        return;
-    }
-    (void)telnet_cmd(&te);
-    if (c_will_term) {
-        // terminal type sub-negotiation
-        // IAC SB TERMINAL-TYPE SEND IAC SE
-        te.putc(nIAC); te.putc(sSB); te.putc(oTERM);
-        te.putc(sSEND); te.putc(nIAC); te.putc(sSE);
-        te.flush_out();
-        // accept terminal type response
-        te.nextc(&c);
-        if (c != nIAC) {
-            te.ungetc();
-            return;
-        }
-        (void)telnet_cmd(&te);
-    }
-
-    // start terminal size negotiation
-    // IAC DO WINDOW-SIZE
-    te.putc(nIAC); te.putc(nDO); te.putc(oNAWS);
-    te.flush_out();
-    te.nextc(&c);
-    if (c != nIAC) {
-        te.ungetc();
-        return;
-    }
-    (void)telnet_cmd(&te);
-    if (c_will_naws) {
-        // accept terminal size response
-        te.nextc(&c);
-        if (c != nIAC) {
-            te.ungetc();
-            return;
-        }
-        (void)telnet_cmd(&te);
-    }
-
-    // SGA/ECHO
-    te.putc(nIAC); te.putc(nWILL); te.putc(oSGA);
-    te.putc(nIAC); te.putc(nDO); te.putc(oSGA);
-    te.putc(nIAC); te.putc(nWILL); te.putc(oECHO);
-    te.flush_out();
-}
-
-//=============================================//
-// relaying of a terminal emulator and a shell //
-//---------------------------------------------//
-void telnet_session(int te_sock, int sh_pty)
-{
-    IOBuf te = te_sock;
-    IOBuf sh = sh_pty;
-    fd_set rtmp, rbits;
-    FD_ZERO(&rtmp);
-    FD_SET(te, &rtmp);
-    FD_SET(sh, &rtmp);
-    u_char c;
-    int cr = 0;
-    int cnt = 0;
-    for (;;) {
-        rbits = rtmp;
-        if (select(FD_SETSIZE, &rbits, 0, 0, 0) <= 0) {
-            break;
-        }
-        if (FD_ISSET(sh, &rbits)) {
-            // send data from a shell to a terminal
-            if (sh.flush_in() == false) {
-                break;
-            }
-            while (sh.getc(&c) == true) {
-                if (c == nIAC) {
-                    // escape a TELNET IAC char
-                    te.putc(c);
-                }
-                te.putc(c);
-            }
-            if (te.flush_out() == false) {
-                break;
-            }
-            if (cnt++ < 20) {
-                continue;  // give priority to data from a shell
-            }
-            cnt = 0;
-        }
-        if (FD_ISSET(te, &rbits)) {
-            // send data from a terminal to a shell
-            if (te.flush_in() == false) {
-                break;
-            }
-            while (te.getc(&c) == true) {
-                if (c == nIAC && !dumb) {
-                    u_char cmd = telnet_cmd(&te) ;
-                    if (cmd == oNAWS) {
-                        // resize pty by terminal size change notice
-                        ioctl(sh_pty, TIOCSWINSZ, &win_size);
-                        continue;
-                    }
-                    if (cmd != nIAC) {
-                        continue;
-                    }
-                } else if (c == '\r') {
-                    cr = 1;
-                } else if (c == '\n' || c == '\0') {
-                    if (cr) {  // do not send LF or NUL just after CR
-                        cr = 0;
-                        continue;
-                    }
-                } else {
-                    cr = 0;
-                }
-                sh.putc(c);
-            }
-            if (sh.flush_out() == false) {
-                break;
-            }
-        }
-    }
-}
-
-//=========================================================//
-// connection of TELNET terminal emulator and Cygwin shell //
-//---------------------------------------------------------//
-int main(int argc, char** argv)
-{
-    int listen_sock = -1;
-    u_short listen_port;
-    int te_sock = -1;
-    int sh_pty = -1;
-    HANDLE hTerm = NULL;
-    int sh_pid, agent_pid = 0;
-
-    // load configuration
-    load_cfg();
-
-    // read commandline arguments
-    get_args(argc, argv);
-
-    if (cmd_shell[0] == 0) {
-        msg_print("missing shell");
-        return 0;
-    }
-    if (cmd_term[0] == 0 && cl_port <= 0) {
-        msg_print("missing terminal");
-        return 0;
-    }
-
-    if (change_dir[0] != 0) {
-	home_chdir = false;
-	if (enable_loginshell) {
-	    add_env(&sh_envp, "CHERE_INVOKING=y", NULL);
-	}
-    }
-
-    // terminal side connection
-    if (cl_port > 0) {
-        // connect to the specified TCP port
-        if ((te_sock = connect_client()) < 0) {
-            goto cleanup;
-        }
-    } else {
-        // prepare a TELNET listener socket
-        if ((listen_sock = listen_telnet(&listen_port)) < 0) {
-            goto cleanup;
-        }
-        in_addr addr;
-        addr.s_addr = htonl(INADDR_LOOPBACK);
-        char tmp[256];
-        debug_msg_print("execute terminal");
-        snprintf(tmp, sizeof(tmp), cmd_term, inet_ntoa(addr), (int)ntohs(listen_port));
-        snprintf(cmd_term, sizeof(cmd_term), "%s %s", tmp, cmd_termopt);
-
-        // execute a terminal emulator
-        if ((hTerm = exec_term()) == NULL) {
-            api_error("exec_term failed");
-            goto cleanup;
-        }
-        // accept connection from the terminal emulator
-        if ((te_sock = accept_telnet(listen_sock)) < 0) {
-            goto cleanup;
-        }
-        shutdown(listen_sock, 2);
-        close(listen_sock);
-        listen_sock = -1;
-    }
-    // TELNET negotiation
-    if (!dumb) {
-        telnet_nego(te_sock);
-    }
-
-    // execute ssh-agent proxy
-    if (enable_agent_proxy) {
-        agent_pid = exec_agent_proxy();
-    }
-
-    // execute a shell
-    debug_msg_print("execute shell");
-    if ((sh_pty = exec_shell(&sh_pid)) < 0) {
-        debug_msg_print("exec_shell failed");
-        goto cleanup;
-    }
-    // set initial pty window size
-    if (!dumb && c_will_naws && win_size.ws_col != 0) {
-        ioctl(sh_pty, TIOCSWINSZ, &win_size);
-    }
-
-    debug_msg_print("entering telnet session");
-    // relay the terminal emulator and the shell
-    telnet_session(te_sock, sh_pty);
-
-  cleanup:
-    if (agent_pid > 0) {
-        kill(agent_pid, SIGTERM);
-    }
-    if (sh_pty >= 0) {
-        close(sh_pty);
-        kill(sh_pid, SIGKILL);
-    }
-    if (agent_pid > 0 || sh_pty >= 0) {
-        wait((int*)NULL);
-    }
-    if (listen_sock >= 0) {
-        shutdown(listen_sock, 2);
-        close(listen_sock);
-    }
-    if (te_sock >= 0) {
-        shutdown(te_sock, 2);
-        close(te_sock);
-    }
-    if (hTerm != NULL) {
-        WaitForSingleObject(hTerm, INFINITE);
-        CloseHandle(hTerm);
-    }
-    return 0;
-}
-
-#ifdef NO_WIN_MAIN
-// This program is an Win32 application but, start as Cygwin main().
-//------------------------------------------------------------------
-extern "C" {
-    void mainCRTStartup(void);
-    void WinMainCRTStartup(void) { mainCRTStartup(); }
-};
-#endif
-
-//EOF
+/////////////////////////////////////////////////////////////////////////////
+// CygTerm+ - yet another Cygwin console
+// Copyright (C) 2000-2006 NSym.
+// (C) 2006- TeraTerm Project
+//---------------------------------------------------------------------------
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License (GPL) as published by
+// the Free Software Foundation; either version 2 of the License, or (at
+// your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//---------------------------------------------------------------------------
+
+/////////////////////////////////////////////////////////////////////////////
+// CygTerm+ - yet another Cygwin console
+//
+//   Using Cygwin with a terminal emulator.
+//
+//   Writtern by TeraTerm Project.
+//            https://ttssh2.osdn.jp/
+//
+//   Original written by NSym.
+//
+
+#if !defined(__CYGWIN__)
+#error check compiler
+#endif
+
+// MessageBox\x82̃^\x83C\x83g\x83\x8B\x82Ŏg\x97p TODO exe\x83t\x83@\x83C\x83\x8B\x96\xBC\x82ɕύX
+static char Program[] = "CygTerm+";
+//static char Version[] = "version 1.07_30_beta (2021/11/14)";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <windows.h>
+#include <shlobj.h>
+#include <pwd.h>
+#include <sys/select.h>
+#include <wchar.h>
+
+#include "sub.h"
+
+#include "cygterm_cfg.h"
+
+// pageant support (ssh-agent proxy)
+//----------------------------------
+#define AGENT_COPYDATA_ID 0x804e50ba
+#define AGENT_MAX_MSGLEN 8192
+char sockdir[] = "/tmp/ssh-XXXXXXXXXX";
+char sockname[256];
+
+// PTY device name
+//----------------
+#define  DEVPTY  "/dev/ptmx"
+
+// TCP port for TELNET
+//--------------------
+#define PORT_START_DEFAULT 20000  // default lowest port number
+#define PORT_RANGE_DEFAULT 40     // default number of ports
+
+// TCP port for connection to another terminal application
+//--------------------------------------------------------
+int cl_port = 0;
+u_short listen_port;
+
+// telnet socket timeout
+//----------------------
+#define TELSOCK_TIMEOUT_DEFAULT 5   // timeout 5 sec
+
+// chdir to HOME
+//--------------
+#define HOME_CHDIR_DEFAULT false
+
+// login shell flag
+//-----------------
+#define ENABLE_LOGINSHELL_DEFAULT false
+
+// ssh agent proxy
+//----------------
+#define ENABLE_AGENT_PROXY_DEFAULT false
+
+// debug mode
+//-----------
+#define DEBUG_FLAG_DEFAULT false;
+bool debug_flag = DEBUG_FLAG_DEFAULT;
+
+// "cygterm.cfg"
+static char *cfg_base;          // "cygterm.cfg"
+static char *cfg_exe;           // [exe directory]/cygterm.cfg
+static char *conf_appdata_full; // $APPDATA/teraterm5/cygterm.cfg
+static char *sys_conf;          // /etc/cygterm.conf
+static char *usr_conf;          // ~/cygtermrc  $HOME/cygtermrc
+
+//================//
+// message output //
+//----------------//
+// msg \x82\xCD ANSI\x95\xB6\x8E\x9A\x83R\x81[\x83h (UTF8\x82͉\xBB\x82\xAF\x82\xE9)
+void msg_print(const char* msg)
+{
+    OutputDebugStringA(msg);
+    MessageBoxA(NULL, msg, Program, MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
+}
+
+//=========================//
+// Win32-API error message //
+//-------------------------//
+void api_error(const char* string = NULL)
+{
+    char msg[1024];
+    char *ptr = msg;
+    if (string != NULL)
+        ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string);
+    FormatMessageA(
+        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        ptr, sizeof(msg)-(ptr-msg), NULL
+    );
+    msg_print(msg);
+}
+
+//=========================//
+// C-runtime error message //
+//-------------------------//
+void c_error(const char* string = NULL)
+{
+    char msg[1024];
+    char *ptr = msg;
+    if (string != NULL)
+        ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string);
+    snprintf(ptr, sizeof(msg)-(ptr-msg), "%s\n", strerror(errno));
+    msg_print(msg);
+}
+
+//======================//
+// debug message output //
+//======================//
+void debug_msg_print(const char* msg, ...)
+{
+	if (debug_flag) {
+		char *tmp1;
+		va_list arg;
+		va_start(arg, msg);
+		vasprintf(&tmp1, msg, arg);
+		va_end(arg);
+
+		char *tmp2;
+		unsigned long pid = GetCurrentProcessId();
+		asprintf(&tmp2, "dbg %lu: %s\n", pid, tmp1);
+		OutputDebugStringA(tmp2);
+		// printf("%s", tmp2);
+		free(tmp2);
+		free(tmp1);
+	}
+}
+
+static void get_cfg_filenames()
+{
+	char *argv0 = GetModuleFileNameU8();
+	// cfg base filename "cygterm.cfg"
+	char *p = strrchr(argv0, '.');
+	*p = 0;	// cut ".exe"
+	p = strrchr(argv0, '/') + 1;
+	cfg_base = (char *)malloc(strlen(p) + 5);
+	strcpy(cfg_base, p);
+	strcat(cfg_base, ".cfg");
+
+	// exe path
+	cfg_exe = (char *)malloc(strlen(argv0) + strlen(cfg_base));
+	strcpy(cfg_exe, argv0);
+	p = strrchr(cfg_exe, '/') + 1;
+	strcpy(p, cfg_base);
+	free(argv0);
+	argv0 = NULL;
+
+	// home	 $HOME/cygtermrc
+	const char *home = getenv("HOME");
+	usr_conf = (char *)malloc(strlen(home) + strlen(cfg_base) + 2);
+	strcpy(usr_conf, home);
+	strcat(usr_conf, "/.");
+	strcat(usr_conf, cfg_base);
+	p = strrchr(usr_conf, '.');		// ".cfg" -> "rc"
+	strcpy(p, "rc");
+
+	// system
+	sys_conf = (char *)malloc(sizeof("/etc/") + strlen(cfg_base) + 2);
+	strcpy(sys_conf, "/etc/");
+	strcat(sys_conf, cfg_base);
+	p = strrchr(sys_conf, '.');
+	strcpy(p, ".conf");		// ".cfg" -> ".conf"
+
+	// $APPDATA/teraterm5/cygterm.cfg
+	char *appdata = GetAppDataDirU8();
+	const char *teraterm = "/teraterm5/";
+	size_t len = strlen(appdata) + strlen(teraterm) + strlen(cfg_base) + 1;
+	conf_appdata_full = (char *)malloc(sizeof(char) * len);
+	strcpy(conf_appdata_full, appdata);
+	strcat(conf_appdata_full, teraterm);
+	strcat(conf_appdata_full, cfg_base);
+	free(appdata);
+}
+
+/**
+ *  read /etc/passwd
+ *  get user name from getlogin().  if it fails, use $USERNAME instead.
+ *  and get /etc/passwd information by getpwnam(3) with user name,
+ */
+static void get_username_and_shell(cfg_data_t *cfg)
+{
+	const char* username = getlogin();
+	if (username == NULL)
+		username = getenv("USERNAME");
+	if (username != NULL) {
+		struct passwd* pw_ent = getpwnam(username);
+		if (pw_ent != NULL) {
+			free(cfg->shell);
+			cfg->shell = strdup(pw_ent->pw_shell);
+			free(cfg->username);
+			cfg->username = strdup(pw_ent->pw_name);
+		}
+		else {
+			free(cfg->username);
+			cfg->username = strdup(username);
+		}
+	}
+}
+
+#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+
+//====================//
+// load configuration //
+//--------------------//
+static void load_cfg(cfg_data_t *cfg)
+{
+	// \x90ݒ\xE8\x83t\x83@\x83C\x83\x8B\x93ǂݍ\x9E\x82ݏ\x87
+	//		\x83\x8A\x83X\x83g\x82̏\xE3\x82̂ق\xA4\x82\xA9\x82\xE7\x90\xE6\x82ɓǂݍ\x9E\x82܂\xEA\x82\xE9
+	//		\x83\x8A\x83X\x83g\x82̉\xBA\x82̂ق\xA4\x82\xA9\x82\xE7\x8C\xE3\x82ɓǂݍ\x9E\x82܂\xEA\x82\xE9(\x82\xA0\x82Ə\x9F\x82\xBF)
+	//		\x89\xBA\x82̕\xFB\x82\xAA\x97D\x90揇\x88ʂ\xAA\x8D\x82\x82\xA2
+	// \x92ʏ펞
+	char *conf_order_normal_list[] = {
+		cfg_exe,			// [exe directory]/cygterm.cfg
+		sys_conf,			// /etc/cygterm.conf
+		conf_appdata_full,	// $APPDATA/teraterm5/cygterm.cfg
+		usr_conf			// ~/cygtermrc
+	};
+	const int conf_order_normal_count = (int)_countof(conf_order_normal_list);
+
+	// \x83|\x81[\x83^\x83u\x83\x8B\x8E\x9E
+	char *conf_order_portable_list[] = {
+		cfg_exe,			// [exe directory]/cygterm.cfg
+	};
+	const int conf_order_portable_count = (int)_countof(conf_order_portable_list);
+
+	char **conf_order_list;
+	int conf_order_count;
+	if (IsPortableMode()) {
+		// \x83|\x81[\x83^\x83u\x83\x8B\x8E\x9E
+		conf_order_list = conf_order_portable_list;
+		conf_order_count = conf_order_portable_count;
+	}
+	else {
+		// \x92ʏ펞
+		conf_order_list = conf_order_normal_list;
+		conf_order_count = conf_order_normal_count;
+	}
+
+	// \x8E\xC0\x8Dۂɓǂݍ\x9E\x82\xDE
+	for (int i = 0; i < conf_order_count; i++) {
+		const char *fname = conf_order_list[i];
+		debug_msg_print("load %s", fname);
+		// ignore empty configuration file path
+		if (fname == NULL || strcmp(fname, "") == 0) {
+			debug_msg_print("  pass");
+			continue;
+		}
+
+		bool r = cfg->load(cfg, fname);
+		debug_msg_print("  %s", r ? "ok" : "ng");
+		cfg->dump(cfg, debug_msg_print);
+	}
+}
+
+void quote_cut(char *dst, size_t len, char *src) {
+	while (*src && len > 1) {
+		if (*src != '"') {
+			*dst++ = *src;
+		}
+		src++;
+	}
+	*dst = 0;
+}
+
+//=======================//
+// commandline arguments //
+//-----------------------//
+void get_args(char** argv, cfg_data_t *cfg)
+{
+    for (++argv; *argv != NULL; ++argv) {
+        if (!strcmp(*argv, "-t")) {             // -t <terminal emulator>
+            if (*++argv == NULL)
+                break;
+            free(cfg->term);
+            cfg->term = strdup(*argv);
+        }
+        else if (!strcmp(*argv, "-p")) {        // -p <port#>
+            if (*(argv+1) != NULL) {
+                ++argv;
+                cfg->cl_port = atoi(*argv);
+            }
+        }
+        else if (!strcmp(*argv, "-dumb")) {     // -dumb
+            cfg->dumb = 1;
+            free(cfg->term_type);
+            cfg->term_type = strdup("dumb");
+        }
+        else if (!strcmp(*argv, "-s")) {        // -s <shell>
+            if (*++argv == NULL)
+                break;
+            if (strcasecmp(*argv, "AUTO") != 0) {
+                free(cfg->shell);
+                cfg->shell = strdup(*argv);
+            }
+        }
+        else if (!strcmp(*argv, "-cd")) {       // -cd
+            cfg->home_chdir = true;
+        }
+        else if (!strcmp(*argv, "-nocd")) {     // -nocd
+            cfg->home_chdir = false;
+        }
+        else if (!strcmp(*argv, "+cd")) {       // +cd
+            cfg->home_chdir = false;
+        }
+        else if (!strcmp(*argv, "-ls")) {       // -ls
+            cfg->enable_loginshell = true;
+        }
+        else if (!strcmp(*argv, "-nols")) {     // -nols
+            cfg->enable_loginshell = false;
+        }
+        else if (!strcmp(*argv, "+ls")) {       // +ls
+            cfg->enable_loginshell = false;
+        }
+        else if (!strcmp(*argv, "-A")) {       // -A
+            cfg->enable_agent_proxy = true;
+        }
+        else if (!strcmp(*argv, "-a")) {       // -a
+            cfg->enable_agent_proxy = false;
+        }
+        else if (!strcmp(*argv, "-v")) {        // -v <additional env var>
+            if (*(argv+1) != NULL) {
+                sh_env_t *sh_env = cfg->sh_env;
+                ++argv;
+                sh_env->add1(sh_env, *argv);
+            }
+        }
+        else if (!strcmp(*argv, "-d")) {        // -d <exec directory>
+            if (*++argv == NULL)
+                break;
+            char change_dir[256] = "";
+            quote_cut(change_dir, sizeof(change_dir), *argv);
+            cfg->change_dir = strdup(change_dir);
+        }
+        else if (!strcmp(*argv, "-o")) {        // -o <additional option for terminal>
+            if (*++argv == NULL)
+                break;
+            free(cfg->termopt);
+            cfg->termopt = strdup(*argv);
+        }
+        else if (!strcmp(*argv, "-debug")) {    // -debug
+            cfg->debug_flag = true;
+        }
+    }
+}
+
+//===================================//
+// pageant support (ssh-agent proxy) //
+//-----------------------------------//
+unsigned long get_uint32(unsigned char *buff)
+{
+	return ((unsigned long)buff[0] << 24) +
+	       ((unsigned long)buff[1] << 16) +
+	       ((unsigned long)buff[2] <<  8) +
+	       ((unsigned long)buff[3]);
+}
+
+void set_uint32(unsigned char *buff, unsigned long v)
+{
+	buff[0] = (unsigned char)(v >> 24);
+	buff[1] = (unsigned char)(v >> 16);
+	buff[2] = (unsigned char)(v >>  8);
+	buff[3] = (unsigned char)v;
+	return;
+}
+
+unsigned long agent_request(unsigned char *out, unsigned long out_size, unsigned char *in)
+{
+	HWND hwnd;
+	char mapname[25];
+	HANDLE fmap = NULL;
+	unsigned char *p = NULL;
+	COPYDATASTRUCT cds;
+	unsigned long len;
+	unsigned long ret = 0;
+
+	if (out_size < 5) {
+		return 0;
+	}
+	if ((len = get_uint32(in)) > AGENT_MAX_MSGLEN) {
+		goto agent_error;
+	}
+
+	hwnd = FindWindowA("Pageant", "Pageant");
+	if (!hwnd) {
+		goto agent_error;
+	}
+
+	sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());
+	fmap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+							  0, AGENT_MAX_MSGLEN, mapname);
+	if (!fmap) {
+		goto agent_error;
+	}
+
+	if ((p = (unsigned char *)MapViewOfFile(fmap, FILE_MAP_WRITE, 0, 0, 0)) == NULL) {
+		goto agent_error;
+	}
+
+	cds.dwData = AGENT_COPYDATA_ID;
+	cds.cbData = strlen(mapname) + 1;
+	cds.lpData = mapname;
+
+	memcpy(p, in, len + 4);
+	if (SendMessageA(hwnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds) > 0) {
+		len = get_uint32(p);
+		if (out_size >= len + 4) {
+			memcpy(out, p, len + 4);
+			ret = len + 4;
+		}
+	}
+
+agent_error:
+	if (p) {
+		UnmapViewOfFile(p);
+	}
+	if (fmap) {
+		CloseHandle(fmap);
+	}
+	if (ret == 0) {
+		set_uint32(out, 1);
+		out[4] = 5; // SSH_AGENT_FAILURE
+	}
+
+	return ret;
+}
+
+void sighandler(int sig) {
+	(void)sig;
+	unlink(sockname);
+	rmdir(sockdir);
+	exit(0);
+};
+
+struct connList {
+	int sock;
+	int recvlen;
+	int sendlen;
+	struct connList *next;
+	unsigned char ibuff[AGENT_MAX_MSGLEN];
+	unsigned char obuff[AGENT_MAX_MSGLEN];
+};
+
+int proc_recvd(struct connList *conn)
+{
+	int reqlen, len;
+
+	if (conn->sendlen > 0) {
+		return 0;
+	}
+
+	if (conn->recvlen < 4) {
+		return 0;
+	}
+
+	reqlen = get_uint32(conn->ibuff) + 4;
+	if (conn->recvlen < reqlen) {
+		return 0;
+	}
+
+	len = agent_request(conn->obuff, sizeof(conn->obuff), conn->ibuff);
+
+	if (len > 0) {
+		conn->sendlen = len;
+	}
+	else {
+		set_uint32(conn->obuff, 1);
+		conn->obuff[4] = 5; // SSH_AGENT_FAILURE
+		conn->sendlen = 1;
+	}
+
+	if (conn->recvlen == reqlen) {
+		conn->recvlen = 0;
+	}
+	else {
+		conn->recvlen -= reqlen;
+		memmove(conn->ibuff, conn->ibuff + reqlen, conn->recvlen);
+	}
+
+	return 1;
+}
+
+void agent_proxy()
+{
+	int sock, asock, ret;
+	long len;
+	struct sockaddr_un addr;
+	struct connList connections, *new_conn, *prev, *cur;
+	fd_set readfds, writefds, rfds, wfds;
+	struct sigaction act;
+	sigset_t blk;
+
+	connections.next = NULL;
+
+	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+		c_error("agent_proxy: socket failed.");
+		exit(0);
+	}
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, sockname, sizeof(addr.sun_path));
+
+	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		goto agent_thread_cleanup;
+	}
+	if (listen(sock, -1) < 0) {
+		goto agent_thread_cleanup;
+	}
+
+	sigfillset(&blk);
+	sigdelset(&blk, SIGKILL);
+	sigdelset(&blk, SIGSTOP);
+
+	memset(&act, 0, sizeof(act));
+	act.sa_handler = sighandler;
+	act.sa_mask = blk;
+	sigaction(SIGINT, &act, NULL);
+	sigaction(SIGTERM, &act, NULL);
+	sigaction(SIGHUP, &act, NULL);
+	sigaction(SIGQUIT, &act, NULL);
+
+	FD_ZERO(&readfds);
+	FD_ZERO(&writefds);
+	FD_SET(sock, &readfds);
+
+	while (1) {
+		memcpy(&rfds, &readfds, sizeof(fd_set));
+		memcpy(&wfds, &writefds, sizeof(fd_set));
+
+		select(FD_SETSIZE, &rfds, &wfds, NULL, NULL);
+
+		if (FD_ISSET(sock, &rfds)) {
+			asock = accept(sock, NULL, NULL);
+			if (asock < 0) {
+				if (!(errno == EINTR || errno == ECONNABORTED)) {
+					break;
+				}
+			}
+			else {
+				new_conn = (struct connList *)malloc(sizeof(struct connList));
+				if (new_conn == NULL) {
+					// no memory
+					close(sock);
+				}
+				else {
+					new_conn->sock = asock;
+					new_conn->recvlen = 0;
+					new_conn->sendlen = 0;
+					new_conn->next = connections.next;
+					connections.next = new_conn;
+					FD_SET(asock, &readfds);
+				}
+			}
+		}
+
+		prev = &connections;
+		for (cur=connections.next; cur != NULL; cur = cur->next) {
+			if (FD_ISSET(cur->sock, &wfds)) {
+				if (cur->sendlen > 0) {
+					len = send(cur->sock, cur->obuff, cur->sendlen, 0);
+					if (len < 0) {
+						// write error
+						prev->next = cur->next;
+						shutdown(cur->sock, SHUT_RDWR);
+						close(cur->sock);
+						FD_CLR(cur->sock, &writefds);
+						FD_CLR(cur->sock, &readfds);
+						free(cur);
+						cur = prev;
+						continue;
+					}
+					else if (len >= cur->sendlen) {
+						cur->sendlen = 0;
+
+						sigprocmask(SIG_BLOCK, &blk, NULL);
+						ret = proc_recvd(cur);
+						sigprocmask(SIG_UNBLOCK, &blk, NULL);
+
+						if (ret) {
+							FD_SET(cur->sock, &writefds);
+							FD_CLR(cur->sock, &readfds);
+						}
+						else {
+							FD_CLR(cur->sock, &writefds);
+							FD_SET(cur->sock, &readfds);
+						}
+					}
+					else if (len > 0) {
+						cur->sendlen -= len;
+						memmove(cur->obuff, cur->obuff+len, cur->sendlen);
+					}
+				}
+				else {
+					FD_CLR(cur->sock, &writefds);
+				}
+			}
+
+			if (FD_ISSET(cur->sock, &rfds)) {
+				len = recv(cur->sock, cur->ibuff + cur->recvlen, sizeof(cur->ibuff) - cur->recvlen, 0);
+				if (len > 0) {
+					cur->recvlen += len;
+
+					sigprocmask(SIG_BLOCK, &blk, NULL);
+					ret = proc_recvd(cur);
+					sigprocmask(SIG_UNBLOCK, &blk, NULL);
+
+					if (ret) {
+						FD_SET(cur->sock, &writefds);
+						FD_CLR(cur->sock, &readfds);
+					}
+					else {
+						FD_CLR(cur->sock, &writefds);
+						FD_SET(cur->sock, &readfds);
+					}
+				}
+				else if (len <= 0) {
+					// read error
+					prev->next = cur->next;
+					shutdown(cur->sock, SHUT_RDWR);
+					close(cur->sock);
+					FD_CLR(cur->sock, &readfds);
+					FD_CLR(cur->sock, &writefds);
+					free(cur);
+					cur = prev;
+					continue;
+				}
+			}
+		}
+	}
+
+agent_thread_cleanup:
+	shutdown(sock, SHUT_RDWR);
+	close(sock);
+
+	unlink(sockname);
+	rmdir(sockdir);
+
+	exit(0);
+}
+
+static int exec_agent_proxy(sh_env_t *sh_env)
+{
+	int pid;
+
+	if (mkdtemp(sockdir) == NULL) {
+		return -1;
+	}
+	snprintf(sockname, sizeof(sockname), "%s/agent.%ld", sockdir, (long)getpid());
+
+	if (sh_env->add(sh_env, "SSH_AUTH_SOCK", sockname)) {
+		return -1;
+	}
+
+	if ((pid = fork()) < 0) {
+		return -1;
+	}
+	if (pid == 0) {
+		setsid();
+		agent_proxy();
+	}
+	return pid;
+}
+
+//=============================//
+// terminal emulator execution //
+//-----------------------------//
+DWORD WINAPI term_thread(LPVOID param)
+{
+	cfg_data_t *cfg = (cfg_data_t *)param;
+
+	in_addr addr;
+	addr.s_addr = htonl(INADDR_LOOPBACK);
+	char *term;
+	asprintf(&term, cfg->term, inet_ntoa(addr), (int)ntohs(listen_port));
+	if (cfg->termopt != NULL) {
+		char *tmp;
+		asprintf(&tmp, "%s %s", tmp, cfg->termopt);
+		free(term);
+		term = tmp;
+	}
+
+	debug_msg_print("CreateProcess '%s'", term);
+
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+	FillMemory(&si, sizeof(si), 0);
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESHOWWINDOW;
+	si.wShowWindow = SW_SHOW;
+	DWORD flag = 0;
+
+#if defined(UNICODE)
+	wchar_t *termT = ToWcharU8(term);
+#else
+	char *termT = strdup(term);
+#endif
+
+	BOOL r =
+		CreateProcess(
+			NULL, termT, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi);
+	free(termT);
+	if (!r) {
+		api_error(term);
+		return 0;
+	}
+	WaitForSingleObject(pi.hProcess, INFINITE);
+	CloseHandle(pi.hProcess);
+	CloseHandle(pi.hThread);
+	return 0;
+}
+
+//=======================================//
+// thread creation for terminal emulator //
+//---------------------------------------//
+HANDLE exec_term(cfg_data_t *cfg)
+{
+    DWORD id;
+    return CreateThread(NULL, 0, term_thread, cfg, 0, &id);
+}
+
+//=======================================//
+// listener socket for TELNET connection //
+//---------------------------------------//
+int listen_telnet(u_short* port, cfg_data_t *cfg)
+{
+    int lsock;
+    if ((lsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        return -1;
+    }
+    struct sockaddr_in addr;
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    int i;
+    for (i = 0; i < cfg->port_range; ++i) { // find an unused port#
+        addr.sin_port = htons(cfg->port_start + i);
+        if (bind(lsock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
+            break;
+        }
+    }
+    if (i == cfg->port_range) {
+        shutdown(lsock, 2);
+        close(lsock);
+        return -1;
+    }
+    if (listen(lsock, 1) != 0) {
+        shutdown(lsock, 2);
+        close(lsock);
+        return -1;
+    }
+    *port = addr.sin_port;
+    return lsock;
+}
+
+//=============================//
+// accept of TELNET connection //
+//-----------------------------//
+int accept_telnet(int lsock, cfg_data_t *cfg)
+{
+    fd_set rbits;
+    FD_ZERO(&rbits);
+    FD_SET(lsock, &rbits);
+    struct timeval tm;
+    tm.tv_sec = cfg->telsock_timeout;
+    tm.tv_usec = 0;
+    if (select(FD_SETSIZE, &rbits, 0, 0, &tm) <= 0) {
+        c_error("accept_telnet: select failed");
+        return -1;
+    }
+    if (!FD_ISSET(lsock, &rbits)) {
+        c_error("accept_telnet: FD_ISSET failed");
+        return -1;
+    }
+    int asock;
+    struct sockaddr_in addr;
+    int len = sizeof(addr);
+    if ((asock = accept(lsock, (struct sockaddr *)&addr, &len)) < 0) {
+        c_error("accept_telnet: accept failed");
+        return -1;
+    }
+    if (getpeername(asock, (struct sockaddr *)&addr, &len) != 0) {
+        c_error("accept_telnet: getpeername failed");
+        shutdown(asock, 2);
+        close(asock);
+        return -1;
+    }
+    if (addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+        // reject it except local connection
+        msg_print("not local connection");
+        shutdown(asock, 2);
+        close(asock);
+        return -1;
+    }
+    return asock;
+}
+
+//============================//
+// connect to specified port# //
+//----------------------------//
+int connect_client()
+{
+    int csock;
+    if ((csock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        return -1;
+    }
+    struct sockaddr_in addr;
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    addr.sin_port = htons(cl_port);
+    if (connect(csock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        close(csock);
+        return -1;
+    }
+    return csock;
+}
+
+//========================================//
+// setup *argv[] from a string for exec() //
+//----------------------------------------//
+void get_argv(char **argv, int maxc, char *s)
+{
+    int esc, sq, dq;  // recognize (\) (') (") and tokenize
+    int c, argc;
+    char *p;
+    esc = sq = dq = 0;
+    for (argc = 0; argc < maxc-1; ++argc) {
+        for ( ; isascii(*s) && isspace(*s); ++s);
+        if (*s == 0) {
+            break;
+        }
+        argv[argc] = p = s;
+        while ((c = *s) != 0) {
+            ++s;
+            if (isspace(c) && !esc && !sq && !dq) {
+                break;
+            }
+            if (c == '\'' && !esc && !dq) {
+                sq ^= 1;
+            } else if (c == '"' && !esc && !sq) {
+                dq ^= 1;
+            } else if (c == '\\' && !esc) {
+                esc = 1;
+            } else {
+                esc = 0;
+                *p++ = c;
+            }
+        }
+        *p = 0;
+    }
+    // not to judge syntax errors
+    // if (dq || sq || esc) { syntax error }
+    // if (argc == maxc) { overflow }
+    argv[argc] = NULL;
+}
+
+//=================//
+// shell execution //
+//-----------------//
+static int exec_shell(int* sh_pid, cfg_data_t *cfg)
+{
+    // open pty master
+    int master;
+    if ((master = open(DEVPTY, O_RDWR)) < 0) {
+        c_error("exec_shell: master pty open error");
+        return -1;
+    }
+    int pid;
+    if ((pid = fork()) < 0) {
+        c_error("exec_shell: fork failed");
+        return -1;
+    }
+    if (pid == 0) {
+        // detach from control tty
+        setsid();
+        // open pty slave
+        int slave;
+        if ((slave = open(ptsname(master), O_RDWR)) < 0) {
+            c_error("exec_shell: slave pty open error");
+            exit(0);
+        }
+        // stdio redirection
+        while (slave <= 2) {
+            if ((slave = dup(slave)) < 0) {
+                exit(0);
+            }
+        }
+        int fd;
+        for (fd = 0; fd < 3; ++fd) {
+            close(fd);
+            dup(slave);
+            fcntl(fd, F_SETFD, 0);
+        }
+        for (fd = 3; fd < getdtablesize(); ++fd) {
+            if (fcntl(fd, F_GETFD) == 0) {
+                close(fd);
+            }
+        }
+        // set env vars
+        if (cfg->term_type != NULL) {
+            // set terminal type to $TERM
+            setenv("TERM", cfg->term_type, 1);
+        }
+        // set other additional env vars
+        int i = 0;
+        sh_env_t *sh_env = cfg->sh_env;
+        while(1) {
+            char *value;
+            const char *env = sh_env->get(sh_env, i++, &value);
+            if (env == NULL) {
+                break;
+            }
+            setenv(env, value, 1);
+        }
+        // change directory
+        if (cfg->change_dir != NULL) {
+            if (chdir(cfg->change_dir) < 0) {
+                char tmp[256];
+                snprintf(tmp, 256, "exec_shell: Can't chdir to \"%s\".", cfg->change_dir);
+                tmp[255] = 0;
+                c_error(tmp);
+            }
+        }
+        else if (cfg->home_chdir) {
+            // chdir to home directory
+            const char *home_dir = getenv("HOME");
+            // ignore chdir(2) system-call error.
+            chdir(home_dir);
+        }
+        // execute a shell
+        char *argv[32];
+        char *cmd_shell = cfg->shell;
+        get_argv(argv, 32, cmd_shell);
+        cfg->shell = strdup(cmd_shell);
+        if (cfg->enable_loginshell) {
+            char shell_path[128];
+            char *pos;
+            strcpy(shell_path, argv[0]);
+            if ((pos = strrchr(argv[0], '/')) != NULL) {
+                *pos = '-';
+                argv[0] = pos;
+            }
+            debug_msg_print("execv '%s' (login shell)", shell_path);
+            execv(shell_path, argv);
+        }
+        else {
+            debug_msg_print("execv '%s'", argv[0]);
+            execv(argv[0], argv);
+        }
+        // no error, exec() doesn't return
+        c_error(argv[0]);
+        exit(0);
+    }
+    *sh_pid = pid;
+    return master;
+}
+
+//==================//
+// i/o buffer class //
+//------------------//
+class IOBuf
+{
+private:
+    int fd;
+    u_char i_buf[4096];
+    u_char o_buf[4096];
+    int i_pos, i_len, o_pos;
+public:
+    IOBuf(int channel) : fd(channel), i_pos(0), i_len(0), o_pos(0) {}
+    operator int() { return fd; }
+    void ungetc() { --i_pos; }
+    bool flush_in();
+    bool getc(u_char*);
+    bool nextc(u_char*);
+    bool putc(u_char);
+    bool flush_out();
+};
+
+// read bytes into input buffer
+//-----------------------------
+bool IOBuf::flush_in()
+{
+    if ((i_len = read(fd, i_buf, sizeof(i_buf))) <= 0)
+        return false;
+    i_pos = 0;
+    return true;
+}
+
+// get 1 char from input buffer
+//-----------------------------
+inline bool IOBuf::getc(u_char* c)
+{
+    if (i_pos == i_len) return false;
+    *c = i_buf[i_pos++];
+    return true;
+}
+
+// get next 1 char from input buffer
+//----------------------------------
+inline bool IOBuf::nextc(u_char* c)
+{
+    if (i_pos == i_len)
+        if (!flush_in()) return false;
+    *c = i_buf[i_pos++];
+    return true;
+}
+
+// put 1 char to output buffer
+//----------------------------
+inline bool IOBuf::putc(u_char c)
+{
+    if (o_pos == sizeof(o_buf))
+        if (!flush_out()) return false;
+    o_buf[o_pos++] = c;
+    return true;
+}
+
+// write bytes from output buffer
+//-------------------------------
+bool IOBuf::flush_out()
+{
+    int n;
+    for (int i = 0; i < o_pos; i += n) {
+        if ((n = write(fd, o_buf+i, o_pos-i)) <= 0) return false;
+    }
+    o_pos = 0;
+    return true;
+}
+
+//=========================//
+// TELNET command handling //  (see RFC854 TELNET PROTOCOL SPECIFICATION)
+//-------------------------//
+enum { nIAC=255, nWILL=251, nWONT=252, nDO=253, nDONT=254 };
+enum { sSEND=1, sIS=0, sSB=250, sSE=240 };
+enum { oECHO=1, oSGA=3, oTERM=24, oNAWS=31 };
+
+bool c_will_term = false;
+bool c_will_naws = false;
+
+// terminal type & size
+//---------------------
+char *term_type;
+struct winsize win_size = {0,0,0,0};
+
+// dumb terminal flag
+//-------------------
+bool dumb = false;
+
+u_char telnet_cmd(IOBuf* te)
+{
+    u_char cmd, c;
+    te->nextc(&cmd);
+    if (cmd == sSB) {
+        te->nextc(&c);
+        // accept terminal type request
+        if (c == oTERM) {                      // "SB TERM
+            te->nextc(&c);                     //     IS
+            u_char* p = (u_char*)term_type;
+            te->nextc(p);                      //     TERMINAL-TYPE
+            while (*p != nIAC) {
+                if (isupper(*p)) *p = _tolower(*p);
+                ++p; te->nextc(p);
+            }
+            *p = 0;
+            te->nextc(&c);                     //     IAC SE"
+            return (u_char)oTERM;
+        }
+        // accept terminal size request
+        if (c == oNAWS) {                      // "SB NAWS
+            u_short col, row;
+            te->nextc((u_char*)&col);
+            te->nextc((u_char*)&col+1);        //     00 00 (cols)
+            te->nextc((u_char*)&row);
+            te->nextc((u_char*)&row+1);        //     00 00 (rows)
+            te->nextc(&c);
+            te->nextc(&c);                     //     TAC SE"
+            win_size.ws_col = ntohs(col);
+            win_size.ws_row = ntohs(row);
+            return (u_char)oNAWS;
+        }
+        while (c != nIAC) te->nextc(&c);       // "... IAC SE"
+        te->nextc(&c);
+    }
+    else if (cmd == nWILL || cmd == nWONT || cmd == nDO || cmd == nDONT) {
+        u_char c;
+        te->nextc(&c);
+        if (cmd == nWILL && c == oTERM)        // "WILL TERM"
+            c_will_term = true;
+        else if (cmd == nWILL && c == oNAWS)   // "WILL NAWS"
+            c_will_naws = true;
+    }
+    return cmd;
+}
+
+//============================//
+// TELNET initial negotiation //
+//----------------------------//
+void telnet_nego(int te_sock)
+{
+    IOBuf te = te_sock;
+    u_char c;
+
+    // start terminal type negotiation
+    // IAC DO TERMINAL-TYPE
+    te.putc(nIAC); te.putc(nDO); te.putc(oTERM);
+    te.flush_out();
+    te.nextc(&c);
+    if (c != nIAC) {
+        te.ungetc();
+        return;
+    }
+    (void)telnet_cmd(&te);
+    if (c_will_term) {
+        // terminal type sub-negotiation
+        // IAC SB TERMINAL-TYPE SEND IAC SE
+        te.putc(nIAC); te.putc(sSB); te.putc(oTERM);
+        te.putc(sSEND); te.putc(nIAC); te.putc(sSE);
+        te.flush_out();
+        // accept terminal type response
+        te.nextc(&c);
+        if (c != nIAC) {
+            te.ungetc();
+            return;
+        }
+        (void)telnet_cmd(&te);
+    }
+
+    // start terminal size negotiation
+    // IAC DO WINDOW-SIZE
+    te.putc(nIAC); te.putc(nDO); te.putc(oNAWS);
+    te.flush_out();
+    te.nextc(&c);
+    if (c != nIAC) {
+        te.ungetc();
+        return;
+    }
+    (void)telnet_cmd(&te);
+    if (c_will_naws) {
+        // accept terminal size response
+        te.nextc(&c);
+        if (c != nIAC) {
+            te.ungetc();
+            return;
+        }
+        (void)telnet_cmd(&te);
+    }
+
+    // SGA/ECHO
+    te.putc(nIAC); te.putc(nWILL); te.putc(oSGA);
+    te.putc(nIAC); te.putc(nDO); te.putc(oSGA);
+    te.putc(nIAC); te.putc(nWILL); te.putc(oECHO);
+    te.flush_out();
+}
+
+//=============================================//
+// relaying of a terminal emulator and a shell //
+//---------------------------------------------//
+void telnet_session(int te_sock, int sh_pty)
+{
+    IOBuf te = te_sock;
+    IOBuf sh = sh_pty;
+    fd_set rtmp, rbits;
+    FD_ZERO(&rtmp);
+    FD_SET(te, &rtmp);
+    FD_SET(sh, &rtmp);
+    u_char c;
+    int cr = 0;
+    int cnt = 0;
+    for (;;) {
+        rbits = rtmp;
+        if (select(FD_SETSIZE, &rbits, 0, 0, 0) <= 0) {
+            break;
+        }
+        if (FD_ISSET(sh, &rbits)) {
+            // send data from a shell to a terminal
+            if (sh.flush_in() == false) {
+                break;
+            }
+            while (sh.getc(&c) == true) {
+                if (c == nIAC) {
+                    // escape a TELNET IAC char
+                    te.putc(c);
+                }
+                te.putc(c);
+            }
+            if (te.flush_out() == false) {
+                break;
+            }
+            if (cnt++ < 20) {
+                continue;  // give priority to data from a shell
+            }
+            cnt = 0;
+        }
+        if (FD_ISSET(te, &rbits)) {
+            // send data from a terminal to a shell
+            if (te.flush_in() == false) {
+                break;
+            }
+            while (te.getc(&c) == true) {
+                if (c == nIAC && !dumb) {
+                    u_char cmd = telnet_cmd(&te) ;
+                    if (cmd == oNAWS) {
+                        // resize pty by terminal size change notice
+                        ioctl(sh_pty, TIOCSWINSZ, &win_size);
+                        continue;
+                    }
+                    if (cmd != nIAC) {
+                        continue;
+                    }
+                } else if (c == '\r') {
+                    cr = 1;
+                } else if (c == '\n' || c == '\0') {
+                    if (cr) {  // do not send LF or NUL just after CR
+                        cr = 0;
+                        continue;
+                    }
+                } else {
+                    cr = 0;
+                }
+                sh.putc(c);
+            }
+            if (sh.flush_out() == false) {
+                break;
+            }
+        }
+    }
+}
+
+//=========================================================//
+// connection of TELNET terminal emulator and Cygwin shell //
+//---------------------------------------------------------//
+int main(int argc, char** argv)
+{
+    (void)argc;
+    int listen_sock = -1;
+    int te_sock = -1;
+    int sh_pty = -1;
+    HANDLE hTerm = NULL;
+    int sh_pid, agent_pid = 0;
+
+
+    // configuration file (.cfg) path
+    get_cfg_filenames();
+    debug_msg_print("cfg_base %s", cfg_base);
+    debug_msg_print("cfg_exe %s", cfg_exe);
+    debug_msg_print("conf_appdata_full %s", conf_appdata_full);
+    debug_msg_print("sys_conf %s", sys_conf);
+    debug_msg_print("usr_conf %s", usr_conf);
+
+
+    // set default values
+    cfg_data_t *cfg = create_cfg();
+    cfg->port_start = PORT_START_DEFAULT;
+    cfg->port_range = PORT_RANGE_DEFAULT;
+    cfg->telsock_timeout = TELSOCK_TIMEOUT_DEFAULT;
+    cfg->home_chdir = HOME_CHDIR_DEFAULT;
+    cfg->enable_loginshell = ENABLE_LOGINSHELL_DEFAULT;
+    cfg->enable_agent_proxy = ENABLE_AGENT_PROXY_DEFAULT;
+    cfg->debug_flag = DEBUG_FLAG_DEFAULT;
+
+    // load configuration
+    get_username_and_shell(cfg);	// from /etc/passwd
+    cfg->dump(cfg, debug_msg_print);
+    load_cfg(cfg);
+    sh_env_t *sh_env = cfg->sh_env;
+    sh_env->add(sh_env, "SHELL", cfg->shell);
+    sh_env->add(sh_env, "USER", cfg->username);
+    debug_msg_print("loginshell %d", cfg->enable_loginshell);
+    cfg->dump(cfg, debug_msg_print);
+
+    // read commandline arguments
+    get_args(argv, cfg);
+    cfg->dump(cfg, debug_msg_print);
+
+    // restore values
+    debug_flag = cfg->debug_flag;
+
+    if (cfg->shell == NULL) {
+        msg_print("missing shell");
+        return 0;
+    }
+    if (cfg->term == NULL && cl_port <= 0) {
+        msg_print("missing terminal");
+        return 0;
+    }
+
+    if (cfg->change_dir != NULL) {
+        cfg->home_chdir = false;
+        if (cfg->enable_loginshell) {
+            sh_env->add(sh_env, "CHERE_INVOKING", "y");
+        }
+    }
+
+    // terminal side connection
+    if (cfg->cl_port > 0) {
+        // connect to the specified TCP port
+        cl_port = cfg->cl_port;
+        if ((te_sock = connect_client()) < 0) {
+            goto cleanup;
+        }
+    } else {
+        // prepare a TELNET listener socket
+        if ((listen_sock = listen_telnet(&listen_port, cfg)) < 0) {
+            goto cleanup;
+        }
+        debug_msg_print("execute terminal");
+
+        // execute a terminal emulator
+        if ((hTerm = exec_term(cfg)) == NULL) {
+            api_error("exec_term failed");
+            goto cleanup;
+        }
+        // accept connection from the terminal emulator
+        if ((te_sock = accept_telnet(listen_sock, cfg)) < 0) {
+            goto cleanup;
+        }
+        shutdown(listen_sock, 2);
+        close(listen_sock);
+        listen_sock = -1;
+    }
+    // TELNET negotiation
+    term_type = cfg->term_type;
+    dumb = cfg->dumb;
+    if (!dumb) {
+        telnet_nego(te_sock);
+    }
+
+    // execute ssh-agent proxy
+    if (cfg->enable_agent_proxy) {
+        agent_pid = exec_agent_proxy(sh_env);
+    }
+
+    // execute a shell
+    debug_msg_print("execute shell");
+    if ((sh_pty = exec_shell(&sh_pid, cfg)) < 0) {
+        debug_msg_print("exec_shell failed");
+        goto cleanup;
+    }
+    // set initial pty window size
+    if (!dumb && c_will_naws && win_size.ws_col != 0) {
+        ioctl(sh_pty, TIOCSWINSZ, &win_size);
+    }
+
+    debug_msg_print("entering telnet session");
+    // relay the terminal emulator and the shell
+    telnet_session(te_sock, sh_pty);
+
+  cleanup:
+    if (agent_pid > 0) {
+        kill(agent_pid, SIGTERM);
+    }
+    if (sh_pty >= 0) {
+        close(sh_pty);
+        kill(sh_pid, SIGKILL);
+    }
+    if (agent_pid > 0 || sh_pty >= 0) {
+        wait((int*)NULL);
+    }
+    if (listen_sock >= 0) {
+        shutdown(listen_sock, 2);
+        close(listen_sock);
+    }
+    if (te_sock >= 0) {
+        shutdown(te_sock, 2);
+        close(te_sock);
+    }
+    if (hTerm != NULL) {
+        WaitForSingleObject(hTerm, INFINITE);
+        CloseHandle(hTerm);
+    }
+    return 0;
+}
+
+#ifdef NO_WIN_MAIN
+// \x83\x8A\x83\x93\x83N\x8E\x9E\x82\xC9 -mwindows \x82\xF0\x8Ew\x92肵\x82Ă\xA2\x82\xE9\x82̂\xC5
+// \x8E\xC0\x8Ds\x83t\x83@\x83C\x83\x8B\x82\xCD subsystem=windows \x82Ő\xB6\x90\xAC\x82\xB3\x82\xEA\x82Ă\xA2\x82\xE9
+// \x83v\x83\x8D\x83O\x83\x89\x83\x80\x82̃G\x83\x93\x83g\x83\x8A\x82\xCD WinMainCRTStartup() \x82ƂȂ\xE9
+// cygwin\x82ŃC\x83\x93\x83X\x83g\x81[\x83\x8B\x82\xB3\x82\xEA\x82\xE9gcc 11.2 \x82ł͂Ƃ\xAD\x82Ɏw\x92肵\x82Ȃ\xAD\x82Ă\xE0 main() \x82\xAA\x83R\x81[\x83\x8B\x82\xB3\x82\xEA\x82\xE9
+//
+// \x88ȑO\x82\xCCcygwin\x82\xCCgcc\x82ł͎\x9F\x82̃R\x81[\x83h\x82\xAA\x95K\x97v\x82\xBE\x82\xC1\x82\xBD\x82̂\xA9\x82\xE0\x82\xB5\x82\xEA\x82Ȃ\xA2
+// This program is an Win32 application but, start as Cygwin main().
+//------------------------------------------------------------------
+extern "C" {
+    void mainCRTStartup(void);
+    void WinMainCRTStartup(void) { mainCRTStartup(); }
+};
+#endif
+
+//EOF

Deleted: trunk/cygwin/cygterm/cygterm.rc
===================================================================
--- trunk/cygwin/cygterm/cygterm.rc	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/cygwin/cygterm/cygterm.rc	2022-02-05 17:46:17 UTC (rev 9725)
@@ -1 +0,0 @@
-icon ICON cygterm.ico

Added: trunk/cygwin/cygterm/cygterm_cfg.cc
===================================================================
--- trunk/cygwin/cygterm/cygterm_cfg.cc	                        (rev 0)
+++ trunk/cygwin/cygterm/cygterm_cfg.cc	2022-02-05 17:46:17 UTC (rev 9725)
@@ -0,0 +1,406 @@
+/*
+ * (C) 2022- TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "cygterm_cfg.h"
+
+#define DUMP_ENABLE	1
+
+// additional env vars given to a shell
+//-------------------------------------
+
+typedef struct sh_env_data_t {
+	struct sh_env_data_t* next;
+	char *name;
+	char *value;
+} sh_env_data_t;
+
+typedef struct sh_env_private_tag {
+	sh_env_data_t *envp;
+} sh_env_private_t;
+
+static bool env_add(sh_env_t *envp, const char* name, const char* value)
+{
+	sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data;
+	sh_env_data_t* e;
+
+	if (name[0] == 0) {
+		return true;
+	}
+	e = (sh_env_data_t*)malloc(sizeof(*e));
+	if (e == NULL) {
+		return false;
+	}
+
+	e->name = strdup(name);
+	e->value = strdup(value);
+	e->next = NULL;
+
+	if (pr_data->envp == NULL) {
+		pr_data->envp = e;
+		return true;
+	}
+	sh_env_data_t* env_data = pr_data->envp;
+	sh_env_data_t* prev_env = NULL;
+	while(1) {
+		if (strcmp(env_data->name, name) == 0) {
+			// \x93\xAF\x82\xB6\x96\xBC\x91O -> \x93\xFC\x82\xEA\x91ւ\xA6
+			if (prev_env == NULL) {
+				pr_data->envp = e;
+			} else {
+				prev_env->next = e;
+				e->next = env_data->next;
+			}
+			free(env_data->name);
+			free(env_data->value);
+			free(env_data);
+			break;
+		}
+		if (env_data->next == NULL) {
+			// \x8DŌ\xE3\x82܂ŗ\x88\x82\xBD
+			env_data->next = e;
+			break;
+		}
+		prev_env = env_data;
+		env_data = env_data->next;
+	}
+
+	return true;
+}
+
+static bool env_add1(sh_env_t *envp, const char* name_value)
+{
+	if (name_value[0] == 0) {
+		return true;
+	}
+	char *name = strdup(name_value);
+	char *p = strchr(name, '=');
+	char *value;
+	if (p == NULL) {
+		value = NULL;
+	}
+	else {
+		*p = 0;
+		value = strdup(p+1);
+	}
+	bool r = env_add(envp, name, value);
+	free(value);
+	free(name);
+	return r;
+}
+
+static char *env_get(sh_env_t *envp, int index, char **value)
+{
+	sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data;
+	sh_env_data_t* e = pr_data->envp;
+	if (e == NULL) {
+		return NULL;
+	}
+	while(1) {
+		if (index == 0) {
+			*value = e->value;
+			return e->name;;
+		}
+		if (e->next == NULL) {
+			*value = NULL;
+			return NULL;
+		}
+		index--;
+		e = e->next;
+	}
+}
+
+static void env_destry_all(sh_env_t *envp)
+{
+	sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data;
+	sh_env_data_t* e = pr_data->envp;
+	if (e == NULL) {
+		return;
+	}
+	pr_data->envp = NULL;
+	while(1) {
+		sh_env_data_t* e_next = e->next;
+		e->next = NULL;
+		free(e);
+		e = e_next;
+		if (e == NULL) {
+			break;
+		}
+	}
+}
+
+static void env_destry(sh_env_t *envp)
+{
+	sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data;
+	env_destry_all(envp);
+	free(pr_data);
+	envp->private_data = NULL;
+	free(envp);
+}
+
+sh_env_t *create_sh_env(void)
+{
+	sh_env_t *sh_env = (sh_env_t *)calloc(sizeof(*sh_env), 1);
+	if (sh_env == NULL) {
+		return NULL;
+	}
+	sh_env_private_t *pr_data = (sh_env_private_t *)calloc(sizeof(*pr_data), 1);
+	if (pr_data == NULL) {
+		free(sh_env);
+		return NULL;
+	}
+	sh_env->private_data = pr_data;
+	sh_env->destroy = env_destry;
+	sh_env->add = env_add;
+	sh_env->add1 = env_add1;
+	sh_env->get = env_get;
+
+	return sh_env;
+}
+
+static bool is_bool_string(const char *s)
+{
+	if (strchr("YyTt", *s) != NULL)
+		return true;
+	if (atoi(s) > 0)
+		return true;
+
+	return false;
+}
+
+//==================================//
+// parse line in configuration file //
+//----------------------------------//
+static void parse_cfg_line(char *buf, cfg_data_t *cfg)
+{
+	// "KEY = VALUE" format in each line.
+	// skip leading/trailing blanks. KEY is not case-sensitive.
+	char* p1;
+	for (p1 = buf; isspace(*p1); ++p1);
+	if (!isalpha(*p1)) {
+		return; // comment line with non-alphabet 1st char
+	}
+	char* name = p1;
+	for (++p1; isalnum(*p1) || *p1 == '_'; ++p1);
+	char* p2;
+	for (p2 = p1; isspace(*p2); ++p2);
+	if (*p2 != '=') {
+		return; // igonore line without '='
+	}
+	for (++p2; isspace(*p2); ++p2);
+	char* val = p2;
+	for (p2 += strlen(p2); isspace(*(p2-1)); --p2);
+	*p1 = *p2 = 0;
+
+	if (!strcasecmp(name, "TERM")) {
+		// terminal emulator command line (host:%s, port#:%d)
+		free(cfg->term);
+		cfg->term = strdup(val);
+	}
+	else if (!strcasecmp(name, "SHELL")) {
+		// shell command line
+		if (strcasecmp(val, "AUTO") != 0) {
+			free(cfg->shell);
+			cfg->shell = strdup(val);
+		}
+	}
+	else if (!strcasecmp(name, "PORT_START")) {
+		// minimum port# for TELNET
+		cfg->port_start = atoi(val);
+	}
+	else if (!strcasecmp(name, "PORT_RANGE")) {
+		// number of ports for TELNET
+		cfg->port_range = atoi(val);
+	}
+	else if (!strcasecmp(name, "TERM_TYPE")) {
+		// terminal type name (maybe overridden by TELNET negotiation.)
+		free(cfg->term_type);
+		cfg->term_type = strdup(val);
+	}
+	else if (!strncasecmp(name, "ENV_", 4)) {
+		// additional env vars given to a shell
+		sh_env_t *sh_env = cfg->sh_env;
+		sh_env->add1(sh_env, val);
+	}
+	else if (!strcasecmp(name, "HOME_CHDIR")) {
+		// change directory to home
+		if (is_bool_string(val)) {
+			cfg->home_chdir = true;
+		}
+	}
+	else if (!strcasecmp(name, "LOGIN_SHELL")) {
+		// execute a shell as a login shell
+		if (is_bool_string(val)) {
+			cfg->enable_loginshell = true;
+		}
+	}
+	else if (!strcasecmp(name, "SOCKET_TIMEOUT")) {
+		// telnet socket timeout
+		cfg->telsock_timeout = atoi(val);
+	}
+	else if (!strcasecmp(name, "SSH_AGENT_PROXY")) {
+		// ssh-agent proxy
+		if (is_bool_string(val)) {
+			cfg->enable_agent_proxy = true;
+		}
+	}
+	else if (!strcasecmp(name, "DEBUG")) {
+		// debug mode
+		if (is_bool_string(val)) {
+			cfg->debug_flag = true;
+		}
+	}
+}
+
+typedef struct {
+	sh_env_data_t *env;
+} cfg_private_data_t;
+
+static void destroy(cfg_data_t *cfg_data)
+{
+//	env_destry_all(cfg_data);
+	sh_env_t *sh_env = cfg_data->sh_env;
+	sh_env->destroy(sh_env);
+	cfg_private_data_t *pr_data = (cfg_private_data_t *)cfg_data->private_data;
+	free(pr_data);
+	free(cfg_data);
+}
+
+// read each setting parameter
+// configuration file (.cfg) path
+static bool load_cfg(cfg_data_t *cfg_data, const char *conf)
+{
+	FILE* fp = fopen(conf, "r");
+	if (fp == NULL) {
+		return false;
+	}
+
+	char buf[BUFSIZ];
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		parse_cfg_line(buf, cfg_data);
+	}
+	fclose(fp);
+
+	return true;
+}
+
+#if !defined(offsetof)
+#define offsetof(s,m) ((size_t)&(((s*)0)->m))
+#endif
+
+#if DUMP_ENABLE
+static void dump(cfg_data_t *cfg_data, void (*print)(const char* msg, ...))
+{
+	const static struct {
+		const char *name;
+		size_t offset;
+		char type;
+	} list[] = {
+		{ "username", offsetof(cfg_data_t, username), 's' },
+		{ "term", offsetof(cfg_data_t, term), 's' },
+		{ "termopt", offsetof(cfg_data_t, termopt), 's' },
+		{ "shell", offsetof(cfg_data_t, shell), 's' },
+		{ "term_type", offsetof(cfg_data_t, term_type), 's' },
+		{ "change_dir", offsetof(cfg_data_t, change_dir), 's' },
+		{ "port_start", offsetof(cfg_data_t, port_start), 'i' },
+		{ "port_range", offsetof(cfg_data_t, port_range), 'i' },
+		{ "cl_port", offsetof(cfg_data_t, cl_port), 'i' },
+		{ "home_chdir", offsetof(cfg_data_t, home_chdir), 'b' },
+		{ "enable_loginshell", offsetof(cfg_data_t, enable_loginshell), 'b' },
+		{ "telsock_timeout", offsetof(cfg_data_t, telsock_timeout), 'i' },
+		{ "enable_agent_proxy", offsetof(cfg_data_t, enable_agent_proxy), 'b' },
+		{ "dumb", offsetof(cfg_data_t, dumb), 'b' },
+		{ "debug_flag", offsetof(cfg_data_t, debug_flag), 'b' },
+	};
+	for (int i = 0; i < (int)(sizeof(list)/sizeof(list[0])); i++) {
+		uint8_t *p = (uint8_t *)cfg_data + list[i].offset;
+		switch (list[i].type) {
+		case 's': {
+			char *str = *(char **)p;
+			print("%s=%s", list[i].name, str == NULL ? "NULL" : str);
+			break;
+		}
+		case 'i': {
+			int i2 = *(int *)p;
+			print("%s=%d(0x%x)", list[i].name, i2, i2);
+			break;
+		}
+		case 'b': {
+			bool b = *(bool *)p;
+			print("%s=%i(%s)", list[i].name, b, b ? "true" : "false");
+			break;
+		}
+		default:
+			print("?");
+			break;
+		}
+	}
+
+	sh_env_t *sh_env = cfg_data->sh_env;
+	for(int i = 0;;i++) {
+		char *value;
+		const char *env = sh_env->get(sh_env, i, &value);
+		if (env == NULL) {
+			break;
+		}
+		print("env %d %s=%s", i, env, value);
+	}
+}
+#else
+static void dump(cfg_data_t *cfg_data, void (*print)(const char* msg, ...))
+{
+	(void)cfg_data;
+	(void)print;
+}
+#endif
+
+cfg_data_t *create_cfg(void)
+{
+	cfg_data_t *cfg_data = (cfg_data_t *)calloc(sizeof(*cfg_data), 1);
+	if (cfg_data == NULL) {
+		return NULL;
+	}
+	cfg_private_data_t *pr_data = (cfg_private_data_t *)calloc(sizeof(*pr_data), 1);
+	if (pr_data == NULL) {
+		free(cfg_data);
+		return NULL;
+	}
+
+	// data, func
+	cfg_data->private_data = pr_data;
+	cfg_data->sh_env = create_sh_env();
+	cfg_data->destroy = destroy;
+	cfg_data->load = load_cfg;
+	cfg_data->dump = dump;
+
+	return cfg_data;
+}

Added: trunk/cygwin/cygterm/cygterm_cfg.h
===================================================================
--- trunk/cygwin/cygterm/cygterm_cfg.h	                        (rev 0)
+++ trunk/cygwin/cygterm/cygterm_cfg.h	2022-02-05 17:46:17 UTC (rev 9725)
@@ -0,0 +1,74 @@
+/*
+ * (C) 2022- TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct sh_env_tag {
+	void (*destroy)(struct sh_env_tag *sh_env_tag);
+	bool (*add)(struct sh_env_tag *sh_env_tag, const char *name, const char *value);
+	bool (*add1)(struct sh_env_tag *sh_env_tag, const char *namevalue);
+	char *(*get)(struct sh_env_tag *sh_env_tag, int index, char **value);
+	void *private_data;
+} sh_env_t;
+
+sh_env_t *create_sh_env(void);
+
+typedef struct cfg_data_tag {
+	char *username;
+	char *term;					// ex. "ttermpro.exe %s %d"
+	char *termopt;				// ex. "/KR-UTF8"
+	char *shell;				// ex. "/usr/bin/bash"
+	char *term_type;			// terminal type ex. "vt100"
+	char *change_dir;			// cd \x82\xB5\x82\xBD\x8C\xE3\x81A\x83V\x83F\x83\x8B\x8BN\x93\xAE
+	int port_start;				// default lowest port number
+	int port_range;				// default number of ports
+	int cl_port;
+	bool home_chdir;			// chdir to HOME
+	bool enable_loginshell;		// login shell flag
+	int telsock_timeout;		// telnet socket timeout
+	bool enable_agent_proxy;	// ssh agent proxy
+	bool dumb;
+	bool debug_flag;			// debug mode
+	bool (*save)(struct cfg_data_tag *cfg_data_tag, const char *fname);
+	bool (*load)(struct cfg_data_tag *cfg_data_tag, const char *fname);
+	void (*destroy)(struct cfg_data_tag *cfg_data_tag);
+	void (*dump)(struct cfg_data_tag *cfg_data_tag, void (*print)(const char* msg, ...));
+	sh_env_t *sh_env;
+	void *private_data;
+} cfg_data_t;
+
+cfg_data_t *load_cfg(const char *cfg);
+cfg_data_t *create_cfg(void);
+
+#ifdef __cplusplus
+}
+#endif

Added: trunk/cygwin/cygterm/sub.cpp
===================================================================
--- trunk/cygwin/cygterm/sub.cpp	                        (rev 0)
+++ trunk/cygwin/cygterm/sub.cpp	2022-02-05 17:46:17 UTC (rev 9725)
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2022- TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <wchar.h>
+#include <shlobj.h>
+#define INITGUID
+#include <knownfolders.h>
+#include <tchar.h>
+
+#include "sub.h"
+
+/**
+ *	\x83}\x83\x8B\x83`\x83o\x83C\x83g\x95\xB6\x8E\x9A\x97\xF1\x82\xF0wchar_t\x95\xB6\x8E\x9A\x97\xF1\x82֕ϊ\xB7
+ *	@param[in]	*str_ptr	mb(char)\x95\xB6\x8E\x9A\x97\xF1
+ *	@param[in]	str_len		mb(char)\x95\xB6\x8E\x9A\x97\xF1\x92\xB7(0\x82̂Ƃ\xAB\x8E\xA9\x93\xAE\x81A\x8E\xA9\x93\xAE\x82̂Ƃ\xAB\x82\xCD'\0'\x82Ń^\x81[\x83~\x83l\x81[\x83g\x82\xB7\x82邱\x82\xC6)
+ *	@param[in]	code_page	\x95ϊ\xB7\x8C\xB3\x83R\x81[\x83h\x83y\x81[\x83W
+ *	@param[out]	*w_len_		wchar_t\x95\xB6\x8E\x9A\x97\xF1\x92\xB7,wchar_t\x90\x94,'\0'\x82\xF0\x95ϊ\xB7\x82\xB5\x82\xBD\x82\xE7L'\0'\x82\xE0\x8A܂\xDE
+ *							(NULL\x82̂Ƃ\xAB\x95\xB6\x8E\x9A\x97񒷂\xF0\x95Ԃ\xB3\x82Ȃ\xA2)
+ *	@retval		wchar_t\x95\xB6\x8E\x9A\x97\xF1\x82ւ̃|\x83C\x83\x93\x83^(NULL\x82̎\x9E\x95ϊ\xB7\x83G\x83\x89\x81[)
+ *				\x8Eg\x97p\x8C\xE3 free() \x82\xB7\x82邱\x82\xC6
+ */
+static wchar_t *_MultiByteToWideChar(const char *str_ptr, size_t str_len, int code_page, size_t *w_len_)
+{
+	DWORD flags = MB_ERR_INVALID_CHARS;
+	if (code_page == CP_ACP) {
+		code_page = (int)GetACP();
+	}
+	if (code_page == CP_UTF8) {
+		// CP_UTF8 When this is set, dwFlags must be zero.
+		flags = 0;
+	}
+	if (w_len_ != NULL) {
+		*w_len_ = 0;
+	}
+	if (str_len == 0) {
+		str_len = strlen(str_ptr) + 1;
+	}
+	int len = ::MultiByteToWideChar(code_page, flags,
+									str_ptr, (int)str_len,
+									NULL, 0);
+	if (len == 0) {
+		return NULL;
+	}
+	wchar_t *wstr_ptr = (wchar_t *)malloc(len*sizeof(wchar_t));
+	if (wstr_ptr == NULL) {
+		return NULL;
+	}
+	len = ::MultiByteToWideChar(code_page, flags,
+								str_ptr, (int)str_len,
+								wstr_ptr, len);
+	if (len == 0) {
+		free(wstr_ptr);
+		return NULL;
+	}
+	if (w_len_ != NULL) {
+		// \x95ϊ\xB7\x82\xB5\x82\xBD\x95\xB6\x8E\x9A\x97\xF1\x90\x94(wchar_t\x90\x94)\x82\xF0\x95Ԃ\xB7
+		*w_len_ = len;
+	}
+	return wstr_ptr;
+}
+
+/**
+ *	wchar_t\x95\xB6\x8E\x9A\x97\xF1\x82\xF0\x83}\x83\x8B\x83`\x83o\x83C\x83g\x95\xB6\x8E\x9A\x97\xF1\x82֕ϊ\xB7
+ *	\x95ϊ\xB7\x82ł\xAB\x82Ȃ\xA2\x95\xB6\x8E\x9A\x82\xCD '?' \x82ŏo\x97͂\xB7\x82\xE9
+ *
+ *	@param[in]	*wstr_ptr	wchar_t\x95\xB6\x8E\x9A\x97\xF1
+ *	@param[in]	wstr_len	wchar_t\x95\xB6\x8E\x9A\x97\xF1\x92\xB7(0\x82̂Ƃ\xAB\x8E\xA9\x93\xAE\x81A\x8E\xA9\x93\xAE\x82̂Ƃ\xAB\x82\xCDL'\0'\x82Ń^\x81[\x83~\x83l\x81[\x83g\x82\xB7\x82邱\x82\xC6)
+ *	@param[in]	code_page	\x95ϊ\xB7\x90\xE6\x83R\x81[\x83h\x83y\x81[\x83W
+ *	@param[out]	*mb_len_	\x95ϊ\xB7\x82\xB5\x82\xBD\x95\xB6\x8E\x9A\x97\xF1\x92\xB7,byte\x90\x94,L'\0'\x82\xF0\x95ϊ\xB7\x82\xB5\x82\xBD\x82\xE7'\0'\x82\xE0\x8A܂\xDE
+ *							(NULL\x82̂Ƃ\xAB\x95\xB6\x8E\x9A\x97񒷂\xF0\x95Ԃ\xB3\x82Ȃ\xA2)
+ *	@retval		mb\x95\xB6\x8E\x9A\x97\xF1\x82ւ̃|\x83C\x83\x93\x83^(NULL\x82̎\x9E\x95ϊ\xB7\x83G\x83\x89\x81[)
+ *				\x8Eg\x97p\x8C\xE3 free() \x82\xB7\x82邱\x82\xC6
+ */
+static char *_WideCharToMultiByte(const wchar_t *wstr_ptr, size_t wstr_len, int code_page, size_t *mb_len_)
+{
+	const DWORD flags = 0;
+	char *mb_ptr;
+	if (code_page == CP_ACP) {
+		code_page = (int)GetACP();
+	}
+	if (mb_len_ != NULL) {
+		*mb_len_ = 0;
+	}
+	if (wstr_len == 0) {
+		wstr_len = wcslen(wstr_ptr) + 1;
+	}
+	size_t len = ::WideCharToMultiByte(code_page, flags,
+									   wstr_ptr, (DWORD)wstr_len,
+									   NULL, 0,
+									   NULL, NULL);
+	if (len == 0) {
+		return NULL;
+	}
+	mb_ptr = (char *)malloc(len);
+	if (mb_ptr == NULL) {
+		return NULL;
+	}
+	len = ::WideCharToMultiByte(code_page, flags,
+								wstr_ptr, (DWORD)wstr_len,
+								mb_ptr, (int)len,
+								NULL,NULL);
+	if (len == 0) {
+		free(mb_ptr);
+		return NULL;
+	}
+	if (mb_len_ != NULL) {
+		// \x95ϊ\xB7\x82\xB5\x82\xBD\x95\xB6\x8E\x9A\x97\xF1\x90\x94(byte\x90\x94)\x82\xF0\x95Ԃ\xB7
+		*mb_len_ = len;
+	}
+	return mb_ptr;
+}
+
+char *ToCharW(const wchar_t *strW)
+{
+	if (strW == NULL) return NULL;
+	char *strA = _WideCharToMultiByte(strW, 0, CP_ACP, NULL);
+	return strA;
+}
+
+wchar_t *ToWcharA(const char *strA)
+{
+	if (strA == NULL) return NULL;
+	wchar_t *strW = _MultiByteToWideChar(strA, 0, CP_ACP, NULL);
+	return strW;
+}
+
+wchar_t *ToWcharU8(const char *strU8)
+{
+	if (strU8 == NULL) return NULL;
+	wchar_t *strW = _MultiByteToWideChar(strU8, 0, CP_UTF8, NULL);
+	return strW;
+}
+
+char *ToU8W(const wchar_t *strW)
+{
+	if (strW == NULL) return NULL;
+	char *strU8 = _WideCharToMultiByte(strW, 0, CP_UTF8, NULL);
+	return strU8;
+}
+
+/**
+ *	GetModuleFileNameW() \x82̓\xAE\x93I\x83o\x83b\x83t\x83@\x94\xC5
+ *
+ *	@param buf	\x95\xB6\x8E\x9A\x97\xF1\x82\xF0\x8Ai\x94[\x82\xB7\x82\xE9\x83o\x83b\x83t\x83@
+ *				\x95s\x97v\x82ɂȂ\xC1\x82\xBD\x82\xE7free()\x82\xB7\x82\xE9
+ *	@return	\x83G\x83\x89\x81[\x83R\x81[\x83h,0(=NO_ERROR)\x82̂Ƃ\xAB\x83G\x83\x89\x81[\x82Ȃ\xB5
+ */
+DWORD hGetModuleFileNameT(HMODULE hModule, TCHAR **buf)
+{
+	size_t size = MAX_PATH;
+	TCHAR *b = (TCHAR *)malloc(sizeof(TCHAR) * size);
+	DWORD error;
+	if (b == NULL) {
+		error = ERROR_NOT_ENOUGH_MEMORY;
+		goto error_return;
+	}
+
+	for(;;) {
+		DWORD r = GetModuleFileName(hModule, b, (DWORD)size);
+		if (r == 0) {
+			// \x8A֐\x94\x82\xAA\x8E\xB8\x94s
+			error = GetLastError();
+			break;
+		} else if (r < size - 1) {
+			// \x8E擾\x90\xAC\x8C\xF7
+			size = r + 1;
+			b = (TCHAR *)realloc(b, sizeof(TCHAR) * size);
+			*buf = b;
+			return NO_ERROR;
+		} else {
+			size *= 2;
+			TCHAR *p = (TCHAR *)realloc(b, sizeof(TCHAR) * size);
+			if (p == NULL) {
+				free(b);
+				error = ERROR_NOT_ENOUGH_MEMORY;
+				break;
+			}
+			b = p;
+		}
+    }
+
+	// error
+	free(b);
+error_return:
+	*buf = NULL;
+	return error;
+}
+
+/**
+ *	\x83|\x81[\x83^\x83u\x83\x8B\x94łƂ\xB5\x82ē\xAE\x8D삷\x82邩
+ *
+ *	@retval		TRUE		\x83|\x81[\x83^\x83u\x83\x8B\x94\xC5
+ *	@retval		FALSE		\x92ʏ\xED\x83C\x83\x93\x83X\x83g\x81[\x83\x8B\x94\xC5
+ */
+BOOL IsPortableMode(void)
+{
+	return FALSE;
+#if 0
+	static BOOL called = FALSE;
+	static BOOL ret_val = FALSE;
+	if (called == FALSE) {
+		called = TRUE;
+		TCHAR *exe;
+		hGetModuleFileNameT(NULL, &exe);
+#if UNICODE
+		wchar_t *exe_path = wcsdup(exe);
+#else
+		wchar_t *exe_path = ToWcharA(exe);
+#endif
+		wchar_t *bs = wcsrchr(exe_path, L'\\');
+		*bs = 0;
+		const wchar_t *portable_ini_base = L"\\portable.ini";
+		size_t len = wcslen(exe_path) + wcslen(portable_ini_base) + 1;
+		wchar_t *portable_iniW = (wchar_t *)malloc(sizeof(wchar_t) * len);
+		wcscpy(portable_iniW, exe_path);
+		wcscat(portable_iniW, portable_ini_base);
+#if UNICODE
+		DWORD r = GetFileAttributesW(portable_iniW);
+#else
+		char *portable_iniA = ToCharW(portable_iniW);
+		DWORD r = GetFileAttributesA(portable_iniA);
+		free(portable_iniA);
+#endif
+		free(portable_iniW);
+		if (r == INVALID_FILE_ATTRIBUTES) {
+			//\x83t\x83@\x83C\x83\x8B\x82\xAA\x91\xB6\x8D݂\xB5\x82Ȃ\xA2
+			ret_val = FALSE;
+		}
+		else {
+			ret_val = TRUE;
+		}
+	}
+	return ret_val;
+#endif
+}
+
+// $APPDATA wchar_t
+static wchar_t *get_appdata_dir(void)
+{
+#if _WIN32_WINNT > 0x0600
+	wchar_t *appdata;
+	SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appdata);
+	wchar_t *retval = wcsdup(appdata);
+	CoTaskMemFree(appdata);
+	return retval;
+#else
+	LPITEMIDLIST pidl;
+	HRESULT r = SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl);
+	if (r == NOERROR) {
+		wchar_t appdata[MAX_PATH];
+		SHGetPathFromIDListW(pidl, appdata);
+		wchar_t *retval = wcsdup(appdata);
+		CoTaskMemFree(pidl);
+		return retval;
+	}
+	char *env = getenv("APPDATA");
+	if (env == NULL) {
+		// \x82\xE0\x82\xC1\x82ƌÂ\xA2 windows ?
+		abort();
+	}
+	wchar_t *appdata = ToWcharA(env);
+	return appdata;
+#endif
+}
+
+// L'\\' -> L'/'
+static void convert_bsW(wchar_t *path)
+{
+	wchar_t *p = path;
+	while(*p != 0) {
+		if (*p == L'\\') {
+			*p = L'/';
+		}
+		p++;
+	}
+}
+
+// cygwin \x8C\xFC\x82\xAF $APPDATA utf-8 \x82\xF0\x95Ԃ\xB7
+char *GetAppDataDirU8()
+{
+	wchar_t *appdataW = get_appdata_dir();
+	convert_bsW(appdataW);
+	char *appdataU8 = ToU8W(appdataW);
+	free(appdataW);
+	return appdataU8;
+}
+
+/**
+ *	full path exe\x83t\x83@\x83C\x83\x8B\x96\xBC\x82\xF0\x95Ԃ\xB7
+ */
+char *GetModuleFileNameU8(void)
+{
+	TCHAR *buf;
+	hGetModuleFileNameT(NULL, &buf);
+	convert_bsW(buf);
+#if UNICODE
+	char *exe = ToU8W(buf);
+#else
+	char *exe = strdup(buf);
+#endif
+	return exe;
+}

Added: trunk/cygwin/cygterm/sub.h
===================================================================
--- trunk/cygwin/cygterm/sub.h	                        (rev 0)
+++ trunk/cygwin/cygterm/sub.h	2022-02-05 17:46:17 UTC (rev 9725)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022- TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+BOOL IsPortableMode(void);
+char *GetAppDataDirU8(void);
+wchar_t *ToWcharU8(const char *strU8);
+char *ToU8W(const wchar_t *strW);
+char *GetModuleFileNameU8(void);

Modified: trunk/doc/ja/html/setup/folder.md
===================================================================
--- trunk/doc/ja/html/setup/folder.md	2022-02-05 17:46:06 UTC (rev 9724)
+++ trunk/doc/ja/html/setup/folder.md	2022-02-05 17:46:17 UTC (rev 9725)
@@ -1,40 +1,54 @@
 # Tera Term が使用するフォルダ
 
-## デフォルトで使用するフォルダ
+|                  | インストール版(*1)         | ポータブル版                 |
+|------------------|----------------------------|------------------------------|
+| 設定ファイル     | `%APPDATA%\teraterm5`      | exeファイルのあるフォルダ    |
+| 動作ログ、ダンプ | `%LOCALAPPDATA%\teraterm5` | `%USERPROFILE%\Documents\teraterm5` (*2) |
 
+- *1 インストール先 (32bitOS+32bitEXEの場合) `%PROGRAMFILES%\teraterm5`
+- *2 SHGetKnownFolderPath(FOLDERID_Documents) + "\teraterm5"
+
+# ポータブル版について
+
+- 環境(システム設定、個人設定)に左右されず使用することができる
+  - 次のような用途を想定
+    - USBメモリに入れて、そこから起動する
+    - インストールせずに使用する (コピーするだけで使えるようになる)
+- 実行する ttermpro.exe と同じフォルダに portable.ini ファイルを置くことでポータブル版として動作
+- portable.ini の中身はなんでもよい(サイズ0でよい)
+
+# インストール版の使用するフォルダ,ファイルについて
+
+*注意*
+現在 Tera Term 5 は Windows10,11 のみをターゲットとしている
+
+## Windows 10, 11 のフォルダ例
+
 - 設定ファイル
   - `%APPDATA%\teraterm5`
 - 動作ログ、ダンプ
   - `%LOCALAPPDATA%\teraterm5`
-- 実行ファイル(32bitOS+32bitEXEの場合)
-  - `%PROGRAMFILES%\teraterm5`
 
-### Windows Vista以降 の場合のフォルダ例
+## Windows Vista以降 の場合のフォルダ例
 
 - 設定ファイル
   - `C:\Users\[usernane]\AppData\Roaming\teraterm5`
 - 動作ログ、ダンプ
   - `C:\Users\[usernane]\AppData\Local\teraterm5`
-- 実行ファイル(32bitOS+32bitEXE)
-  - `C:\Program Files\teraterm5`
 
-### Windows 2000, XP の場合のフォルダ例
+## Windows 2000, XP の場合のフォルダ例
 
 - 設定ファイル
   - `C:\Documents and Settings\[usernane]\AppData\Roaming\teraterm5`
 - 動作ログ、ダンプ(未定義)
   - `C:\Documents and Settings\[usernane]\Application Data\teraterm5`
-- 実行ファイル
-  - `C:\Program Files\teraterm5`
 
-### Windows 2000より以前の場合のフォルダ例
+## Windows 2000より以前の場合のフォルダ例
 
 - 設定ファイル
   - `C:\My Documents\teraterm5`
-- 動作ログ、ダンプ(未定義)
+- 動作ログ、ダンプ
   - `C:\My Documents\teraterm5`
-- 実行ファイル
-  - `C:\Program Files\teraterm5`
 
 参考
 
@@ -56,4 +70,3 @@
 - 設定ファイルを置くフォルダを作成する
 - ttermpro.exeのフォルダにある設定ファイルを
 - 設定ファイルを置くフォルダにコピーする
-


ttssh2-commit メーリングリストの案内
Back to archive index