File jd-2.8.9-150226-a171004.patch of Package jd-api

このファイルの簡単な説明

  これはjdを5ch.netに読み書き出来るようにするパッチです。

  使用方法
    1. このパッチを当てます
      ex) xzcat jd-2.8.9-150226-aYYMMDD.patch.xz | patch -p1
    2. jdをビルドします
    3. jdを起動します
    4. 設定(C)→about:config 高度な設定(C)を開きます
    5. ■ネットワークの辺りをテキトーに設定します
    6. jdを終了します
    7. jdを再び起動します
    8-1. APIを使う場合はファイル(F)→5chにログイン(L)を実行します
    8-2. read.cgiを使う場合は設定(C)→一般(G)→5chでスレ取得にread.cgiを使う

  ライセンス等
    GNU General Public License, version 2
    このパッチの一切の権利を「JD Project」に譲渡します。

  その他
    予期しない問題が起こることがあるかもしれません。
    本パッチ作者はソフトウェアの正常動作を保証しません。

  以上

diff -aurNZ jd-2.8.9-150226/autogen.sh jd-2.8.9-a171004/autogen.sh
--- jd-2.8.9-150226/autogen.sh	2006-12-18 22:15:45.000000000 +0900
+++ jd-2.8.9-a171004/autogen.sh	2017-04-11 13:23:01.714104000 +0900
@@ -44,7 +44,7 @@
 fi
 
 if test -z "$AM_POSTFIX" ; then
-if ! which automake > /dev/null 2&1 ; then
+if ! which automake > /dev/null 2>&1 ; then
 	echo 'you need automake (1.8.3+ recommended) to generate the Makefile'
 	exit 1
 fi
diff -aurNZ jd-2.8.9-150226/configure.ac jd-2.8.9-a171004/configure.ac
--- jd-2.8.9-150226/configure.ac	2013-02-10 20:38:45.000000000 +0900
+++ jd-2.8.9-a171004/configure.ac	2017-05-04 17:38:57.869030332 +0900
@@ -3,7 +3,7 @@
 dnl
 AC_PREREQ(2.50)
 AC_INIT(jd, 1.0)
-AM_INIT_AUTOMAKE
+AM_INIT_AUTOMAKE([1.13 -Wno-portability])
 AC_CONFIG_HEADERS(config.h)
 
 AC_ISC_POSIX
@@ -29,35 +29,27 @@
 dnl 
 case "${host_os}" in 
  freebsd*) 
-   echo "os = freebsd"
    AC_DEFINE(ICONV_CONST, const, "iconv_const")
    AC_DEFINE(USE_MKTIME, , "use mktime")
    ;;
  solaris*)
-   echo "os = solaris"
    AC_DEFINE(ICONV_CONST, const, "iconv_const")
    AC_DEFINE(NO_TIMEGM, , "no timegm")
    ;;
  mingw*)
-   echo "os = ${host_os}"
    AC_DEFINE(ICONV_CONST, , "iconv_const")
    AC_DEFINE(USE_MKTIME, , "use mktime")
    ;;
  darwin*)
-   echo "os = ${host_os}"
-   AC_DEFUN([AM_ICONV], [])
    AC_DEFINE(ICONV_CONST, , "iconv_const")
    AC_DEFINE(USE_MKTIME, , "use mktime")
    ;;
 dnl linux*|gnu*|*-gnu
  *)
-   echo "os = ${host_os}"
-   AC_DEFUN([AM_ICONV], [])
    AC_DEFINE(ICONV_CONST, , "iconv_const")
    ;;
 esac
 
-AM_ICONV
 
 
 dnl ---------------------------------------------------
@@ -67,7 +59,7 @@
 dnl
 
 dnl 追加コンパイルオプション
-CXXFLAGS="$CXXFLAGS -ggdb -Wall"
+CXXFLAGS="$CXXFLAGS -Wall"
 
 dnl ---------------------------------------------------
 dnl ---------------------------------------------------
@@ -81,19 +73,14 @@
 dnl パッケージのチェック
 dnl
 PKG_CHECK_MODULES(GTKMM,[gtkmm-2.4 >= 2.4.0], [], [])
-PKG_CHECK_MODULES(GTHREAD, [gthread-2.0 >= 2.0] )
 
 AC_SUBST(GTKMM_CFLAGS)
 AC_SUBST(GTKMM_LIBS)
-AC_SUBST(GTHREAD_CFLAGS)
-AC_SUBST(GTHREAD_LIBS)
 
 
 dnl 
 dnl crypt
 dnl
-
-echo "use crypt"
 AC_CHECK_HEADERS([crypt.h])
 AC_CHECK_LIB(crypt,crypt)
 
@@ -101,13 +88,28 @@
 dnl
 dnl zlib
 dnl
-
-echo "use zlib"
 AC_CHECK_HEADERS([zlib.h])
 AC_CHECK_LIB(z,inflate)
 
 
 dnl
+dnl iconv
+dnl
+found_iconv=no
+AC_CHECK_FUNC(iconv_open, [found_iconv=yes])
+if test $found_iconv = "no"; then
+  AC_CHECK_LIB(iconv, libiconv_open, [found_iconv=yes])
+
+  if test $found_iconv = "no"; then
+    AC_CHECK_LIB(iconv, iconv_open, ,AC_MSG_ERROR([iconv implementation not found]))
+  fi
+
+  ICONV_LIBS="-liconv"
+  AC_SUBST(ICONV_LIBS)
+fi
+
+
+dnl
 dnl packages dependent on platform 
 dnl
 use_windres=""
@@ -119,22 +121,14 @@
    dnl not available uname on windows
    dnl
    
-   echo "use winsock2"
    AC_CHECK_HEADERS([winsock2.h])
    AC_CHECK_LIB(ws2_32,_head_libws2_32_a)
    
-   echo "use regex2"
-   AC_CHECK_HEADERS([regex.h])
-   AC_CHECK_LIB(regex,regexec)
-   
-   echo "use iconv"
-   AC_CHECK_HEADERS([iconv.h])
-   AC_CHECK_LIB(iconv,_head_libiconv_a)
+   AC_CHECK_HEADERS([wspiapi.h])
    
-   echo "use windows resources"
    AC_CHECK_TOOL([WINDRES], [windres], [windres])
    AC_SUBST(WINDRES)
-   AC_DEFINE(USE_WINDRES, , "use windres")
+   AC_DEFINE(USE_WINDRES, ,[use windres])
    use_windres="yes"
    ;;
  *)
@@ -142,10 +136,8 @@
    dnl any other POSIX systems
    dnl
 
-   echo "use uname"
    AC_CHECK_HEADERS([sys/utsname.h])
 
-   echo "use socket"
    AC_CHECK_HEADERS([socket.h])
    AC_CHECK_LIB(socket,socket)
    ;;
@@ -179,10 +171,8 @@
 use_gnomeui=no
 
 AC_ARG_WITH(sessionlib,
-[
-  --with-sessionlib[[=xsmp/gnomeui/no]]
-                          use session control library [[default=xsmp]]
-],
+AC_HELP_STRING([--with-sessionlib@<:@=xsmp|gnomeui|no@:>@],
+               [use session control library @<:@default=xsmp@:>@]),
 [case "${withval}" in
   xsmp)
     use_xsmp=yes
@@ -235,10 +225,9 @@
   fi
 
   if test -n "$LIBSM_CFLAGS" -a -n "$LIBSM_LIBS" ; then
-    echo "use XSMP"
+    AC_DEFINE(USE_XSMP, ,[use xsmp])
     AC_SUBST(LIBSM_CFLAGS)
     AC_SUBST(LIBSM_LIBS)
-    CXXFLAGS="$CXXFLAGS -DUSE_XSMP"
   fi
 fi
 
@@ -249,220 +238,247 @@
 
   PKG_CHECK_MODULES(GNOMEUI, [libgnomeui-2.0 >= 2.0] )
 
-  echo "use GNOMEUI"
+  AC_DEFINE(USE_GNOMEUI, ,[use gnomeui])
   AC_SUBST(GNOMEUI_CFLAGS)
   AC_SUBST(GNOMEUI_LIBS)
-  CXXFLAGS="$CXXFLAGS -DUSE_GNOMEUI"
 fi
 
 
 dnl
-dnl SSL
+dnl 正規表現ライブラリ
 dnl
+use_regex=no
+use_pcre=no
+use_onig=no
+
+AC_ARG_WITH(regex,
+AC_HELP_STRING([--with-regex@<:@=posix|pcre|oniguruma|glib@:>@],
+               [use regular expression library @<:@default=posix@:>@]),
+[case "${withval}" in
+  pcre)
+    use_pcre=yes
+    ;;
+  oniguruma)
+    use_onig=yes
+    ;;
+  glib|no)
+    use_regex=no
+    ;;
+  *)
+    use_regex=yes
+    ;;
+esac],use_regex=yes)
 
-use_gnutls=yes
+AC_MSG_CHECKING([regular expression library])
 
-AC_ARG_WITH(openssl,[ --with-openssl    (use openssl)],
-       [ if test "$withval" != "no" ;then
-         use_gnutls=no
-       fi ])
+dnl
+dnl checking posix regex
+dnl
+if test x"$use_regex" = "xyes" ; then
+  AC_MSG_RESULT([posix])
+  AC_CHECK_HEADERS([regex.h], ,[AC_MSG_ERROR([regex.h not found])])
+  AC_CHECK_LIB([regex],[regexec])
+fi
 
-case "${use_gnutls}" in 
- yes)
-   echo "use gnutls"
 
-   PKG_CHECK_MODULES(GNUTLS, [gnutls >= 1.2], [echo "gnutls >= 1.2"])
-   AC_DEFINE(USE_GNUTLS, , "use gnutls")
-   AC_CHECK_HEADERS([gcrypt.h])
-   AC_CHECK_LIB(gcrypt, gcry_md_hash_buffer, [], [AC_MSG_ERROR([gcrypt not found])])
-   AC_SUBST(GNUTLS_CFLAGS)
-   AC_SUBST(GNUTLS_LIBS)
-   ;;
- *)
-   echo "use openssl"
-   PKG_CHECK_MODULES(OPENSSL, [openssl >= 0.9] )
-   AC_DEFINE(USE_OPENSSL, , "use openssl")
-   AC_SUBST(OPENSSL_CFLAGS)
-   AC_SUBST(OPENSSL_LIBS)
-   ;;
-esac
+dnl
+dnl checking PCRE
+dnl
+if test x"$use_pcre" = "xyes" ; then
+  AC_MSG_RESULT([pcre])
+  PKG_CHECK_MODULES(PCRE, [libpcre >= 6.5] )
+  AC_SUBST(PCRE_CFLAGS)
+  AC_SUBST(PCRE_LIBS)
+  AC_CHECK_HEADERS([pcreposix.h], ,[AC_MSG_ERROR([pcreposix.h not found])])
+  AC_CHECK_LIB([pcreposix],[regexec], ,[AC_MSG_ERROR([libpcreposix.a not found])])
+fi
 
 
 dnl
-dnl enable gprof
+dnl checking oniguruma
 dnl
+if test x"$use_onig" = "xyes" ; then
+  AC_MSG_RESULT([oniguruma])
+  AC_CHECK_PROG(ONIG_CONFIG, onig-config, onig-config)
+  if test "x${ONIG_CONFIG}" = "x" ; then
+    AC_MSG_ERROR([onig-config not found])
+  fi
+  ONIG_CFLAGS=`onig-config --cflags`
+  CXXFLAGS="${CXXFLAGS} ${ONIG_CFLAGS}"
+  ONIG_LIBS=`onig-config --libs`
+  LDFLAGS_ORIGINAL="${LDFLAGS}"
+  LDFLAGS="${LDFLAGS} ${ONIG_LIBS}"
+  AC_CHECK_HEADERS([onigposix.h], ,[AC_MSG_ERROR([onigposix.h not found])])
+  AC_CHECK_LIB([onig],[regexec], ,[AC_MSG_ERROR([libonig.a not found])])
+  AC_SUBST(ONIG_LIBS)
+  LDFLAGS="${LDFLAGS_ORIGINAL}"
+fi
 
-use_gprof=no
 
-AC_ARG_ENABLE(gprof,[ --enable-gprof  (enable gprof)],
-       [ if test "$enable_gprof" = "yes"; then
-               echo "use gprof"
-               CXXFLAGS="$CXXFLAGS -pg"
-               use_gprof=yes
-       fi ])
+dnl
+dnl glib regex
+dnl
+if test x"$use_regex" != "xyes" -a x"$use_pcre" != "xyes" -a x"$use_onig" != "xyes" ; then
+  AC_MSG_RESULT([glib])
+  PKG_CHECK_MODULES(GLIB,[glib-2.0 >= 2.14.0], ,[regular expression library not found])
+fi
+
 
 dnl
-dnl checking migemo
+dnl TLS
 dnl
-AC_ARG_WITH(migemo,[ --with-migemo    (enable migemo search)],
-       [ if test "$withval" != "no" ;then
-               echo "use migemo"
-               AC_CHECK_HEADERS([migemo.h])
-               AC_CHECK_LIB(migemo,migemo_open)
-       fi ])
 
+use_gnutls=no
+use_openssl=no
+use_nss=no
 
-AC_ARG_WITH(migemodict,[ --with-migemodict    (specifiy the path of migemo dictionary)],
-       [ if test "$withval" ;then
-               echo "migemodict = $withval"
-               AC_DEFINE_UNQUOTED(MIGEMODICT, "$withval" , "migemodict")
-       fi ])
+AC_ARG_WITH(tls,
+AC_HELP_STRING([--with-tls@<:@=gnutls|openssl|nss@:>@],
+               [use TLS library @<:@default=gnutls@:>@]),
+[case "${withval}" in
+  gnutls)
+    use_gnutls=yes
+    ;;
+  openssl)
+    use_openssl=yes
+    ;;
+  nss)
+    use_nss=yes
+    ;;
+esac],use_gnutls=yes)
 
 
 dnl
-dnl checking xdg-open
+dnl checking gnutls
 dnl
-AC_ARG_WITH(xdgopen,[ --with-xdgopen    (use xdg-open as default browser)],
-       [ if test "$withval" ;then
-               echo "use xdg-open"
-               AC_DEFINE(XDGOPEN, , "use xdg-open")
-       fi ])
-
+if test x"$use_gnutls" = "xyes" ; then
+    PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.10.0], ,[AC_MSG_ERROR([gnitls >= 2.10.0 required])])
+    AC_DEFINE(USE_GNUTLS, ,[use gnutls])
+    AC_SUBST(GNUTLS_CFLAGS)
+    AC_SUBST(GNUTLS_LIBS)
+fi
 
 dnl
-dnl checking alsa
+dnl checking openssl
 dnl
-case "${host_os}" in
- linux*|*linux)
- AC_ARG_WITH(alsa,[ --with-alsa (enable alsa)],
-       [ if test "$withval" != "no" ;then
-               echo "use alsa"
-	       PKG_CHECK_MODULES(ALSA, [alsa >= 1.0] )
-	       AC_DEFINE(USE_ALSA, , "use alsa")
-	       AC_SUBST(ALSA_CFLAGS)
-	       AC_SUBST(ALSA_LIBS)
-       fi ])
- ;;
-esac
+if test x"$use_openssl" = "xyes" ; then
+    PKG_CHECK_MODULES(OPENSSL, [openssl >= 0.9.7], ,[AC_MSG_ERROR([openssl >= 0.9.7 required])])
+    AC_DEFINE(USE_OPENSSL, ,[use openssl])
+    AC_SUBST(OPENSSL_CFLAGS)
+    AC_SUBST(OPENSSL_LIBS)
+fi
 
 
 dnl
-dnl checking oniguruma
+dnl checking nss
 dnl
-AC_ARG_WITH(oniguruma,[ --with-oniguruma    (enable oniguruma)],
-       [ if test "$withval" != "no" ;then
-               echo "use oniguruma"
-               AC_CHECK_PROG(ONIG_CONFIG, onig-config, onig-config)
-               if test "x${ONIG_CONFIG}" = "x" ; then
-                 AC_MSG_ERROR([onig-config not found])
-               fi
-               ONIG_CFLAGS=`onig-config --cflags`
-               CPPFLAGS="${CPPFLAGS} ${ONIG_CFLAGS}"
-               ONIG_LIBS=`onig-config --libs`
-               LDFLAGS_ORIGINAL="${LDFLAGS}"
-               LDFLAGS="${LDFLAGS} ${ONIG_LIBS}"
-               AC_CHECK_HEADER([onigposix.h], ,[AC_MSG_ERROR([onigposix.h not found])])
-               AC_CHECK_LIB([onig],[regexec], ,[AC_MSG_ERROR([libonig.a not found])])
-               AC_DEFINE(USE_ONIG, , "use oniguruma")
-               AC_SUBST(ONIG_LIBS)
-               LDFLAGS="${LDFLAGS_ORIGINAL}"
-       fi ])
+if test x"$use_nss" = "xyes" ; then
+    PKG_CHECK_MODULES(NSS, [nss >= 1.0], ,[AC_MSG_ERROR([nss >= 1.0 required])])
+    AC_DEFINE(USE_NSS, ,[use nss])
+    AC_SUBST(NSS_CFLAGS)
+    AC_SUBST(NSS_LIBS)
+fi
 
 
-dnl
-dnl checking PCRE
-dnl
-AC_ARG_WITH(pcre,[ --with-pcre    (enable PCRE)],
-       [ if test "$withval" != "no" ;then
-               echo "use PCRE"
-               PKG_CHECK_MODULES(PCRE, [libpcre >= 6.5] )
-               AC_DEFINE(USE_PCRE, , "use PCRE")
-               AC_SUBST(PCRE_CFLAGS)
-               AC_SUBST(PCRE_LIBS)
-               AC_CHECK_HEADERS([pcreposix.h])
-               AC_CHECK_LIB(pcreposix,regexec)
-       fi ])
+AC_ARG_WITH(rootcert,
+AC_HELP_STRING([--with-rootcert=PATH],[specifiy the path of root certification file]),
+        [ if test x"$withval" != "xno" ;then
+            echo "root cert = $withval"
+            AC_DEFINE_UNQUOTED(DEFAULT_CAFILE, "$withval",[root cert])
+        fi ])
 
 
 dnl
-dnl checking gthread
+dnl enable gprof
 dnl
-AC_ARG_WITH(gthread,[ --with-gthread    (use gthread instead of pthread)],
-       [ if test "$withval" ;then
-               echo "use gthread"
-               AC_DEFINE(USE_GTHREAD, , "use gthread")
-       fi ])
 
+use_gprof=no
+
+AC_ARG_ENABLE(gprof,
+AC_HELP_STRING([--enable-gprof],[enable gprof]),
+        [ if test x"$enable_gprof" != "xno"; then
+            CXXFLAGS="$CXXFLAGS -pg"
+            use_gprof=yes
+        fi ])
 
 dnl
-dnl CPU別の最適化オプション
+dnl checking migemo
 dnl
-if test "$use_gprof" = "no"; then
-
-    dnl
-    dnl checking native (gcc >= 4.2 x86 & x86_64)
-    dnl
-	AC_ARG_WITH(native,[ --with-native    (use native)],
-        [ if test "$withval" != "no"; then
-            echo "use native"
-            CXXFLAGS="$CXXFLAGS -march=native"
+AC_ARG_WITH(migemo,
+AC_HELP_STRING([--with-migemo],[enable migemo search]),
+        [ if test x"$withval" != "xno" ;then
+            AC_CHECK_HEADERS([migemo.h])
+            AC_CHECK_LIB(migemo,migemo_open)
         fi ])
 
-    dnl
-    dnl checking core2duo
-    dnl
-    AC_ARG_WITH(core2duo,[ --with-core2duo    (use core2duo)],
-        [ if test "$withval" != "no"; then
-            echo "use core2duo"
-            CXXFLAGS="$CXXFLAGS -march=pentium-m -msse3"
-        fi ])
 
-    dnl
-    dnl checking athlon64
-    dnl
-    AC_ARG_WITH(athlon64,[ --with-athlon64    (use athlon64)],
-        [ if test "$withval" != "no"; then
-            echo "use athlon64"
-            CXXFLAGS="$CXXFLAGS -march=athlon64"
+AC_ARG_WITH(migemodict,
+AC_HELP_STRING([--with-migemodict=PATH],[specifiy the path of migemo dictionary]),
+        [ if test "$withval" ;then
+            AC_DEFINE_UNQUOTED(MIGEMODICT,"$withval",[migemodict])
         fi ])
 
-    dnl
-    dnl checking atom
-    dnl
-    AC_ARG_WITH(atom,[ --with-atom    (use atom)],
-        [ if test "$withval" != "no"; then
-            echo "use atom"
-            CXXFLAGS="$CXXFLAGS -march=prescott"
+
+dnl
+dnl checking xdg-open
+dnl
+AC_ARG_WITH(xdgopen,
+AC_HELP_STRING([--with-xdgopen],[use xdg-open as default browser]),
+        [ if test x"$withval" != "xno" ;then
+            AC_DEFINE(XDGOPEN, ,[use xdg-open])
         fi ])
 
-    dnl
-    dnl checking ppc7400
-    dnl
-    AC_ARG_WITH(ppc7400,[ --with-ppc7400    (use PowerPC7400)],
-        [ if test "$withval" != "no"; then
-            echo "use ppc7400"
-            CXXFLAGS="$CXXFLAGS -mcpu=7400 -maltivec -mabi=altivec"
+
+dnl
+dnl checking alsa
+dnl
+case "${host_os}" in
+  linux*|*linux)
+  AC_ARG_WITH(alsa,
+  AC_HELP_STRING([--with-alsa],[enable alsa]),
+        [ if test x"$withval" != "xno" ;then
+            PKG_CHECK_MODULES(ALSA, [alsa >= 1.0] )
+            AC_DEFINE(USE_ALSA, ,[use alsa])
+            AC_SUBST(ALSA_CFLAGS)
+            AC_SUBST(ALSA_LIBS)
         fi ])
+ ;;
+esac
+
 
-    dnl
-    dnl checking ppc7450
-    dnl
-    AC_ARG_WITH(ppc7450,[ --with-ppc7450    (use PowerPC7450)],
-        [ if test "$withval" != "no"; then
-            echo "use ppc7450"
-            CXXFLAGS="$CXXFLAGS -mcpu=7450 -maltivec -mabi=altivec"
+dnl
+dnl checking gthread
+dnl
+AC_ARG_WITH(gthread,
+AC_HELP_STRING([--with-gthread],[use gthread instead of pthread]),
+        [ if test x"$withval" != "xno" ;then
+            AC_DEFINE(USE_GTHREAD, ,[use gthread])
         fi ])
-fi
 
 
 dnl
 dnl checking pangolayout
 dnl
-AC_ARG_WITH(pangolayout,[ --with-pangolayout    (use pangolayout)],
-       [ if test "$withval" != "no" ;then
-               echo "use pango_layout for drawing"
-	       CXXFLAGS="$CXXFLAGS -DUSE_PANGOLAYOUT"
-       fi ])
+AC_ARG_WITH(pangolayout,
+AC_HELP_STRING([--with-pangolayout],[use pangolayout]),
+        [ if test x"$withval" != "xno" ;then
+            AC_DEFINE(USE_PANGOLAYOUT, ,[use pangolayout])
+        fi ])
+
+
+dnl
+dnl CPU別の最適化オプション
+dnl
+if test x"$use_gprof" = "xno"; then
+  dnl
+  dnl checking native (gcc >= 4.2 x86 & x86_64)
+  dnl
+  AC_ARG_ENABLE(native,
+  AC_HELP_STRING([--enable-native],[enable optimize for native processor]),
+        [ if test x"$enableval" != "xno"; then
+            CXXFLAGS="$CXXFLAGS -march=native"
+        fi ])
+fi
 
 
 AC_OUTPUT(Makefile src/Makefile src/skeleton/Makefile src/jdlib/Makefile src/dbtree/Makefile src/dbimg/Makefile  src/bbslist/Makefile src/board/Makefile src/article/Makefile src/image/Makefile src/message/Makefile src/history/Makefile src/config/Makefile src/icons/Makefile src/sound/Makefile src/xml/Makefile src/control/Makefile )
diff -aurNZ jd-2.8.9-150226/src/Makefile.am jd-2.8.9-a171004/src/Makefile.am
--- jd-2.8.9-150226/src/Makefile.am	2013-02-18 00:54:24.000000000 +0900
+++ jd-2.8.9-a171004/src/Makefile.am	2016-04-12 22:56:03.110245000 +0900
@@ -18,7 +18,7 @@
 	./sound/libsound.a \
 	./xml/libxml.a \
 	./control/libcontrol.a \
-	@LIBS@ @GTKMM_LIBS@ @GTHREAD_LIBS@ @GNUTLS_LIBS@ @OPENSSL_LIBS@ @GNOMEUI_LIBS@ @LIBSM_LIBS@ @ALSA_LIBS@ @ONIG_LIBS@ @X11_LIBS@
+	@GNOMEUI_LIBS@ @GTKMM_LIBS@ @GNUTLS_LIBS@ @OPENSSL_LIBS@ @NSS_LIBS@ @LIBSM_LIBS@ @ALSA_LIBS@ @ONIG_LIBS@ @X11_LIBS@ @ICONV_LIBS@
 
 
 jd_SOURCES = \
@@ -33,6 +33,7 @@
 	dndmanager.cpp \
 	usrcmdmanager.cpp \
 	linkfiltermanager.cpp \
+	replacestrmanager.cpp \
 	urlreplacemanager.cpp \
 	compmanager.cpp \
 	searchmanager.cpp \
@@ -63,6 +64,7 @@
 	fontcolorpref.cpp \
 	usrcmdpref.cpp \
 	linkfilterpref.cpp \
+	replacestrpref.cpp \
 	livepref.cpp \
 	openurldiag.cpp \
 \
@@ -88,6 +90,7 @@
 	dndmanager.h \
 	usrcmdmanager.h \
 	linkfiltermanager.h \
+	replacestrmanager.h \
     urlreplacemanager.h \
 	compmanager.h \
 	searchmanager.h \
@@ -128,6 +131,7 @@
 	fontcolorpref.h \
 	usrcmdpref.h \
 	linkfilterpref.h \
+	replacestrpref.h \
 	livepref.h \
 	openurldiag.h \
 \
@@ -135,7 +139,7 @@
 	environment.h
 
 
-AM_CXXFLAGS = @GTKMM_CFLAGS@ @GTHREAD_CFLAGS@ @GNUTLS_CFLAGS@ @OPENSSL_CFLAGS@ @GNOMEUI_CFLAGS@ @LIBSM_CFLAGS@
+AM_CXXFLAGS = @GTKMM_CFLAGS@ @GNUTLS_CFLAGS@ @OPENSSL_CFLAGS@ @NSS_CFLAGS@ @GNOMEUI_CFLAGS@ @LIBSM_CFLAGS@
 
 
 jd_windres.o: jdversion.h
diff -aurNZ jd-2.8.9-150226/src/article/articleadmin.cpp jd-2.8.9-a171004/src/article/articleadmin.cpp
--- jd-2.8.9-150226/src/article/articleadmin.cpp	2011-08-08 00:50:52.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleadmin.cpp	2016-02-18 16:58:56.385410000 +0900
@@ -765,7 +765,7 @@
     CORE::DATA_INFO info;
     info.type = TYPE_THREAD;
     info.url = DBTREE::url_readcgi( url, 0, 0 );
-    info.name = DBTREE::article_subject( info.url );
+    info.name = MISC::to_plain( DBTREE::article_modified_subject( info.url ) );
     info.path = Gtk::TreePath( "0" ).to_string();
 
     if( info.url.empty() ) return;
diff -aurNZ jd-2.8.9-150226/src/article/articleview.cpp jd-2.8.9-a171004/src/article/articleview.cpp
--- jd-2.8.9-150226/src/article/articleview.cpp	2015-02-10 20:51:53.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleview.cpp	2016-03-17 22:30:25.116020000 +0900
@@ -86,7 +86,7 @@
     // 閉じたタブ履歴更新
     HISTORY::append_history( URL_HISTCLOSEVIEW,
                              DBTREE::url_dat( get_url() ),
-                             DBTREE::article_subject( get_url() ), TYPE_THREAD );
+                             DBTREE::article_modified_subject( get_url() ), TYPE_THREAD );
 
     CORE::core_set_command( "close_message" ,url_article() );
 
@@ -175,14 +175,14 @@
 
 
 // ロード中
-const bool ArticleViewMain::is_loading()
+const bool ArticleViewMain::is_loading() const
 {
     return get_article()->is_loading();
 }
 
 
 // 更新した
-const bool ArticleViewMain::is_updated()
+const bool ArticleViewMain::is_updated() const
 {
 
 #ifdef _DEBUG
@@ -194,7 +194,7 @@
 
 
 // 更新チェックして更新可能か
-const bool ArticleViewMain::is_check_update()
+const bool ArticleViewMain::is_check_update() const
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewMain::is_check_update " << url_article() << " " << ( get_article()->get_status() & STATUS_UPDATE ) << std::endl;
@@ -204,13 +204,13 @@
 }
 
 // 古いデータか
-const bool ArticleViewMain::is_old()
+const bool ArticleViewMain::is_old() const
 {
     return ( get_article()->get_status() & STATUS_OLD );
 }
 
 // 壊れているか
-const bool ArticleViewMain::is_broken()
+const bool ArticleViewMain::is_broken() const
 {
     return ( get_article()->get_status() & STATUS_BROKEN );
 }
@@ -470,13 +470,13 @@
     if( is_broken() ) str_tablabel = "[ 壊れています ]  ";
     else if( is_old() ) str_tablabel = "[ DAT落ち ]  ";
 
-    if( get_label().empty() || ! str_tablabel.empty() ) set_label( str_tablabel + DBTREE::article_subject( url_article() ) );
+    set_tooltip_label( str_tablabel + MISC::to_markup( DBTREE::article_subject( url_article() ) ) );
+    const std::string& subject = DBTREE::article_modified_subject( url_article() );
+    set_label( str_tablabel + MISC::to_markup( subject ), true );
     ARTICLE::get_admin()->set_command( "redraw_toolbar" );
 
     // タブのラベルセット
-    std::string str_label = DBTREE::article_subject( url_article() );
-    if( str_label.empty() ) str_label = "???";
-    ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), str_label ); 
+    ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), subject.empty() ? "???" : subject );
 
     // タブのアイコン状態を更新
     ARTICLE::get_admin()->set_command( "toggle_icon", get_url() );
@@ -499,7 +499,7 @@
     ARTICLE::get_admin()->set_command( "set_status_color", get_url(), get_color(), force );
 
     // タイトルセット
-    set_title( DBTREE::article_subject( url_article() ) );
+    set_title( MISC::to_plain( subject ) );
     ARTICLE::get_admin()->set_command( "set_title", get_url(), get_title() );
 
     drawarea()->set_enable_draw( true );
@@ -587,7 +587,7 @@
     // 履歴に登録
     if( m_set_history ) HISTORY::append_history( URL_HISTTHREADVIEW,
                                                  DBTREE::url_dat( get_url() ),
-                                                 DBTREE::article_subject( get_url() ), TYPE_THREAD );
+                                                 subject, TYPE_THREAD );
 
     if( m_show_instdialog ) show_instruct_diag();
 
@@ -659,10 +659,10 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewMain::relayout()
+void ArticleViewMain::relayout( const bool completely )
 {
 #ifdef _DEBUG
-    std::cout << "ArticleViewMain::relayout " << DBTREE::article_subject( url_article() ) << std::endl;;
+    std::cout << "ArticleViewMain::relayout " << url_article() << std::endl;;
 #endif
 
     hide_popup( true );
@@ -671,6 +671,8 @@
     int num_reserve = drawarea()->get_goto_num_reserve();
     int separator_new = drawarea()->get_separator_new();
 
+    if( completely ) DBTREE::article_clear_nodetree( url_article() );
+
     drawarea()->clear_screen();
     drawarea()->set_separator_new( separator_new );
     drawarea()->append_res( 1, get_article()->get_number_load() );
diff -aurNZ jd-2.8.9-150226/src/article/articleview.h jd-2.8.9-a171004/src/article/articleview.h
--- jd-2.8.9-150226/src/article/articleview.h	2015-02-10 20:51:53.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleview.h	2016-03-15 21:41:04.094714000 +0900
@@ -45,16 +45,16 @@
 
         virtual void save_session();
 
-        virtual const bool is_loading();
-        virtual const bool is_updated();
-        virtual const bool is_check_update();
-        virtual const bool is_old();
-        virtual const bool is_broken();
+        virtual const bool is_loading() const;
+        virtual const bool is_updated() const;
+        virtual const bool is_check_update() const;
+        virtual const bool is_old() const;
+        virtual const bool is_broken() const;
 
         virtual void show_view();
         virtual void update_view();
         virtual void update_finish();
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
       protected:
 
diff -aurNZ jd-2.8.9-150226/src/article/articleviewbase.cpp jd-2.8.9-a171004/src/article/articleviewbase.cpp
--- jd-2.8.9-150226/src/article/articleviewbase.cpp	2014-03-29 14:23:54.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewbase.cpp	2017-10-02 02:14:09.517518599 +0900
@@ -120,26 +120,12 @@
 //
 // メインウィンドウのURLバーなどの表示用にも使う
 //
-const std::string ArticleViewBase::url_for_copy()
+const std::string ArticleViewBase::url_for_copy() const
 {
     return DBTREE::url_readcgi( m_url_article, 0, 0 );
 }
 
 
-JDLIB::RefPtr_Lock< DBTREE::ArticleBase >& ArticleViewBase::get_article()
-{
-    assert( m_article );
-    return  m_article;
-}
-
-
-DrawAreaBase* ArticleViewBase::drawarea()
-{
-    assert( m_drawarea );
-    return m_drawarea;
-}
-
-
 DrawAreaBase* ArticleViewBase::create_drawarea()
 {
     return Gtk::manage( new ARTICLE::DrawAreaMain( m_url_article ) );
@@ -685,7 +671,7 @@
 //
 // クライアント領域幅
 //
-const int ArticleViewBase::width_client()
+const int ArticleViewBase::width_client() const
 {
 #ifdef _DEBUG
     if( m_drawarea ) std::cout << "ArticleViewBase::width_client : " << m_drawarea->width_client() << std::endl;
@@ -700,7 +686,7 @@
 //
 // クライアント領高さ
 //
-const int ArticleViewBase::height_client()
+const int ArticleViewBase::height_client() const
 {
 #ifdef _DEBUG
     if( m_drawarea ) std::cout << "ArticleViewBase::height_client : " << m_drawarea->height_client() << std::endl;
@@ -713,7 +699,7 @@
 
 
 // アイコンのID取得
-const int ArticleViewBase::get_icon( const std::string& iconname )
+const int ArticleViewBase::get_icon( const std::string& iconname ) const
 {
     int id = ICON::NONE;
 
@@ -1584,7 +1570,7 @@
 
 void ArticleViewBase::slot_setup_abone_board()
 {
-    SKELETON::PrefDiag* pref =  CORE::PrefDiagFactory( get_parent_win(), CORE::PREFDIAG_BOARD, DBTREE::url_subject( m_url_article ), "show_abone_article" );
+    SKELETON::PrefDiag* pref =  CORE::PrefDiagFactory( get_parent_win(), CORE::PREFDIAG_BOARD, DBTREE::url_boardbase( m_url_article ), "show_abone_article" );
     pref->run();
     delete pref;
 }
@@ -1643,11 +1629,10 @@
     if( show_title ){
 
         std::string html;
-        std::string tmpstr = DBTREE::board_name( m_url_article );
-        if( ! tmpstr.empty() ) html += "[ " + tmpstr + " ] ";
+        const std::string& tmpstr = DBTREE::board_name( m_url_article );
+        if( ! tmpstr.empty() ) html += "[ " + MISC::html_escape( tmpstr ) + " ] ";
 
-        tmpstr = DBTREE::article_subject( m_url_article );
-        if( ! tmpstr.empty() ) html += tmpstr;
+        html += DBTREE::article_modified_subject( m_url_article );
 
         if( ! html.empty() ) append_html( html );
     }
@@ -1715,8 +1700,9 @@
     comment << raw_id << "  " << list_resnum.size() << " 件<br>";
     comment << "総参照数:" << m_article->get_res_reference( list_resnum ).size() << " 件";
 
+#if 0
     // 末尾判定
-    if( raw_id.length() == 9 ){
+    if( raw_id.length() == 12 ){
 
         char c = raw_id.c_str()[ 8 ];
         switch( c ){
@@ -1730,6 +1716,7 @@
             case '0': comment << "<br>末尾:" << c << " PC";  break;
         }
     }
+#endif
 
     if( show_option && ! list_resnum.empty() ){
         if( !m_show_url4report ) comment << "<br><br><a href=\"" << PROTO_URL4REPORT << "\">抽出したレスのURLをリスト表示</a>";
@@ -2325,12 +2312,12 @@
         std::string num_str;
 
         const std::string url_dat = DBTREE::url_dat( url, num_from, num_to, num_str );
-        const std::string url_subject = DBTREE::url_subject( url );
+        const std::string boardbase = DBTREE::url_boardbase( url );
 
 #ifdef _DEBUG
         std::cout << "ArticleViewBase::slot_on_url " << url << std::endl;
         std::cout << "url_dat = " << url_dat << std::endl;
-        std::cout << "url_subject = " << url_subject << std::endl;
+        std::cout << "boardbase = " << boardbase << std::endl;
         std::cout << "num_from = " << num_from << std::endl;
         std::cout << "num_to = " << num_to << std::endl;
         std::cout << "num = " << num_str << std::endl;
@@ -2348,11 +2335,9 @@
         }
 
         // 板
-        else if( ! url_subject.empty() ){
-
-            std::string tmpstr = DBTREE::board_name( url );
-            args.arg1 = "[ " + tmpstr + " ] ";
+        else if( ! boardbase.empty() ){
 
+            args.arg1 = "[ " + DBTREE::board_name( url ) + " ] ";
             view_popup = CORE::ViewFactory( CORE::VIEW_ARTICLEPOPUPHTML, m_url_article, args );
         }
     }
@@ -2369,27 +2354,19 @@
         {
             std::string tmp = MISC::url_decode( url );
 
-            const int char_code = MISC::judge_char_code( tmp );
+            const CharCode charcode = MISC::judge_char_code( tmp );
 
-            switch( char_code )
+            switch( charcode )
             {
-                case MISC::CHARCODE_EUC_JP:
-
-                    status_url = MISC::Iconv( tmp, "EUC-JP", "UTF-8" );
-                    break;
-
-                case MISC::CHARCODE_JIS:
-
-                    status_url = MISC::Iconv( tmp, "ISO-2022-JP", "UTF-8" );
-                    break;
-
-                case MISC::CHARCODE_SJIS:
+                case CHARCODE_SJIS:
+                case CHARCODE_EUCJP:
+                case CHARCODE_JIS:
 
-                    status_url = MISC::Iconv( tmp, "MS932", "UTF-8" );
+                    status_url = MISC::Iconv( tmp, charcode, CHARCODE_UTF8 );
                     break;
 
-                case MISC::CHARCODE_ASCII:
-                case MISC::CHARCODE_UTF:
+                case CHARCODE_ASCII:
+                case CHARCODE_UTF8:
 
                     status_url = tmp;
                     break;
@@ -2572,17 +2549,13 @@
 
         hide_popup();
 
-        std::stringstream ssurl;
-        ssurl << "http://be.2ch.net/test/p.php?i="
-              << url.substr( strlen( PROTO_BE ) )
-              << "&u=d:"
-              << DBTREE::url_readcgi( m_url_article, res_number, 0 );
+        std::string openurl = "http://be.5ch.net/user/" + url.substr( strlen( PROTO_BE ) );
 #ifdef _DEBUG
-        std::cout << "open  " << ssurl.str() << std::endl;
+        std::cout << "open  " << openurl << std::endl;
 #endif
-        if( control.button_alloted( event, CONTROL::OpenBeButton ) ) CORE::core_set_command( "open_url_browser", ssurl.str() );
+        if( control.button_alloted( event, CONTROL::OpenBeButton ) ) CORE::core_set_command( "open_url_browser", openurl );
         else if( control.button_alloted( event, CONTROL::PopupmenuBeButton ) ){
-            show_popupmenu( ssurl.str(), false );
+            show_popupmenu( openurl, false );
         }
     }
 
@@ -3425,7 +3398,7 @@
 
     if( query.empty() ) return;
 
-    const std::string url = DBTREE::url_subject( m_url_article );
+    const std::string url = DBTREE::url_boardbase( m_url_article );
 
 #ifdef _DEBUG
     std::cout << "ArticleViewBase::slot_search_cachelocal " << url << std::endl
@@ -3443,7 +3416,7 @@
 {
     if( m_article->empty() ) return;
 
-    CORE::core_set_command( "open_board_next", DBTREE::url_subject( m_url_article ) , m_url_article );
+    CORE::core_set_command( "open_board_next", DBTREE::url_boardbase( m_url_article ) , m_url_article );
 }
 
 
@@ -3684,9 +3657,9 @@
 
     std::string tmpstr = m_url_tmp + "\n";
     if( ref ) tmpstr += CONFIG::get_ref_prefix();
-    std::string board_name = DBTREE::board_name( m_url_article );
+    const std::string& board_name = DBTREE::board_name( m_url_article );
     if( ! board_name.empty() ) tmpstr += "[ " + board_name + " ] ";
-    tmpstr += DBTREE::article_subject( m_url_article ) + "\n\n";
+    tmpstr += MISC::to_plain( DBTREE::article_subject( m_url_article ) ) + "\n\n";
     tmpstr += m_article->get_res_str( atoi( m_str_num.c_str() ), ref );
 
     MISC::CopyClipboard( tmpstr );
@@ -3698,7 +3671,7 @@
 //
 void ArticleViewBase::slot_copy_title_url()
 {
-    MISC::CopyClipboard( DBTREE::article_subject( m_url_article ) + '\n' + url_for_copy() );
+    MISC::CopyClipboard( MISC::to_plain( DBTREE::article_subject( m_url_article ) ) + '\n' + url_for_copy() );
 }
 
 
@@ -3713,7 +3686,7 @@
     info.type = TYPE_THREAD;
     info.parent = ARTICLE::get_admin()->get_win();
     info.url = m_url_article;;
-    info.name = DBTREE::article_subject( m_url_article );
+    info.name = MISC::to_plain( DBTREE::article_modified_subject( m_url_article ) );
     info.path = Gtk::TreePath( "0" ).to_string();
 
     CORE::DATA_INFO_LIST list_info;
diff -aurNZ jd-2.8.9-150226/src/article/articleviewbase.h jd-2.8.9-a171004/src/article/articleviewbase.h
--- jd-2.8.9-150226/src/article/articleviewbase.h	2012-12-10 03:35:41.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewbase.h	2016-03-16 00:21:13.506567000 +0900
@@ -102,10 +102,10 @@
 
         virtual void save_session(){}        
 
-        virtual const std::string url_for_copy(); // コピーやURLバー表示用のURL
-        virtual const int width_client();
-        virtual const int height_client();
-        virtual const int get_icon( const std::string& iconname );
+        virtual const std::string url_for_copy() const; // コピーやURLバー表示用のURL
+        virtual const int width_client() const;
+        virtual const int height_client() const;
+        virtual const int get_icon( const std::string& iconname ) const;
         virtual const bool set_command( const std::string& command,
                                         const std::string& arg1 = std::string(),
                                         const std::string& arg2 = std::string()
@@ -152,14 +152,15 @@
         // 実況モードか
         const bool get_live() const { return m_live; }
 
-        DrawAreaBase* drawarea();
+        DrawAreaBase* drawarea() const { return m_drawarea; }
 
     protected:
 
         // Viewが所属するAdminクラス
         virtual SKELETON::Admin* get_admin();
 
-        JDLIB::RefPtr_Lock< DBTREE::ArticleBase >& get_article();
+        JDLIB::RefPtr_Lock< DBTREE::ArticleBase >& get_article(){ return m_article; };
+        const JDLIB::RefPtr_Lock< DBTREE::ArticleBase >& get_article() const { return m_article; };
 
         // ポップアップメニューを表示する前にメニューのアクティブ状態を切り替える
         virtual void activate_act_before_popupmenu( const std::string& url );
diff -aurNZ jd-2.8.9-150226/src/article/articleviewetc.cpp jd-2.8.9-a171004/src/article/articleviewetc.cpp
--- jd-2.8.9-150226/src/article/articleviewetc.cpp	2010-10-01 00:01:38.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewetc.cpp	2016-02-09 13:31:13.507666000 +0900
@@ -11,6 +11,7 @@
 #include "dbtree/articlebase.h"
 
 #include "control/controlid.h"
+#include "jdlib/miscutil.h"
 
 #include "global.h"
 
@@ -37,7 +38,7 @@
     setup_view();
 
     // ラベル更新
-    set_label( " [ RES:" + m_str_num + " ] - " + DBTREE::article_subject( url_article() ) );
+    set_label( " [ RES:" + m_str_num + " ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -57,7 +58,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewRes::relayout()
+void ArticleViewRes::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewRes::relayout\n";
@@ -118,7 +119,7 @@
     setup_view();
 
     // ラベル更新
-    set_label( " [ 名前:" + m_str_name + " ] - " + DBTREE::article_subject( url_article() ));
+    set_label( " [ 名前:" + m_str_name + " ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -138,7 +139,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewName::relayout()
+void ArticleViewName::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewName::relayout\n";
@@ -196,7 +197,7 @@
     setup_view();
 
     // ラベル更新
-    set_label( " [ " + m_str_id.substr( strlen( PROTO_ID ) ) + " ] - " + DBTREE::article_subject( url_article() ));
+    set_label( " [ " + m_str_id.substr( strlen( PROTO_ID ) ) + " ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -216,7 +217,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewID::relayout()
+void ArticleViewID::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewID::relayout\n";
@@ -272,7 +273,7 @@
     setup_view();
 
     // ラベル更新
-    set_label( " [ しおり ] - " + DBTREE::article_subject( url_article() ));
+    set_label( " [ しおり ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -292,7 +293,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewBM::relayout()
+void ArticleViewBM::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewBM::relayout\n";
@@ -349,7 +350,7 @@
 
 
     // ラベル更新
-    set_label( " [ 書き込み ] - " + DBTREE::article_subject( url_article() ));
+    set_label( " [ 書き込み ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -369,7 +370,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewPost::relayout()
+void ArticleViewPost::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewPost::relayout\n";
@@ -424,7 +425,7 @@
     setup_view();
 
     // ラベル更新
-    set_label( " [ URL ] - " + DBTREE::article_subject( url_article() ));
+    set_label( " [ URL ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -444,7 +445,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewURL::relayout()
+void ArticleViewURL::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewURL::relayout\n";
@@ -503,7 +504,7 @@
     setup_view();
 
     // ラベル更新
-    set_label( " [ Re:" + m_str_num + " ] - " + DBTREE::article_subject( url_article() ));
+    set_label( " [ Re:" + m_str_num + " ] - " + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -523,7 +524,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewRefer::relayout()
+void ArticleViewRefer::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewRefer::relayout\n";
@@ -590,7 +591,7 @@
     std::string str_label;
     if( m_mode_or ) str_label = "[ OR 抽出 ] - ";
     else str_label = "[ AND 抽出 ] - ";
-    set_label( str_label + DBTREE::article_subject( url_article() ) );
+    set_label( str_label + MISC::to_markup( DBTREE::article_modified_subject( url_article() ) ), true );
 
     // タブ更新
     ARTICLE::get_admin()->set_command( "set_tablabel", get_url(), get_label() );
@@ -611,7 +612,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewDrawout::relayout()
+void ArticleViewDrawout::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewDrawout::relayout\n";
@@ -706,7 +707,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewPostlog::relayout()
+void ArticleViewPostlog::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewPostlog::relayout\n";
diff -aurNZ jd-2.8.9-150226/src/article/articleviewetc.h jd-2.8.9-a171004/src/article/articleviewetc.h
--- jd-2.8.9-150226/src/article/articleviewetc.h	2010-09-12 23:29:52.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewetc.h	2016-01-16 16:46:39.763736000 +0900
@@ -22,7 +22,7 @@
         ~ArticleViewRes();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -46,7 +46,7 @@
         ~ArticleViewName();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -70,7 +70,7 @@
         ~ArticleViewID();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -94,7 +94,7 @@
         ~ArticleViewBM();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -119,7 +119,7 @@
         ~ArticleViewPost();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -142,7 +142,7 @@
         ~ArticleViewURL();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -166,7 +166,7 @@
         ~ArticleViewRefer();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -192,7 +192,7 @@
         ~ArticleViewDrawout();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
 
         virtual void show_view();
         virtual void reload();
@@ -215,7 +215,7 @@
         ~ArticleViewPostlog();
 
         // SKELETON::View の関数のオーバロード
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
         virtual void stop(){} // キャンセル
 
         // 検索
diff -aurNZ jd-2.8.9-150226/src/article/articleviewinfo.h jd-2.8.9-a171004/src/article/articleviewinfo.h
--- jd-2.8.9-150226/src/article/articleviewinfo.h	2008-09-22 23:02:39.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewinfo.h	2015-06-20 16:48:50.002171000 +0900
@@ -30,7 +30,7 @@
         virtual bool slot_button_press( std::string url, int res_number, GdkEventButton* event ){ return true; }
 
         // ポップアップ表示キャンセル
-        virtual void slot_on_url( std::string url, int res_number ){}
+        virtual void slot_on_url( std::string url, std::string imgurl, int res_number ){}
 
         virtual DrawAreaBase* create_drawarea();
     };
diff -aurNZ jd-2.8.9-150226/src/article/articleviewsearch.cpp jd-2.8.9-a171004/src/article/articleviewsearch.cpp
--- jd-2.8.9-150226/src/article/articleviewsearch.cpp	2011-05-22 23:57:59.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewsearch.cpp	2016-03-15 18:46:24.238875000 +0900
@@ -186,7 +186,7 @@
 //
 // メインウィンドウのURLバーなどの表示用にも使う
 //
-const std::string ArticleViewSearch::url_for_copy()
+const std::string ArticleViewSearch::url_for_copy() const
 {
     if( m_searchmode == CORE::SEARCHMODE_TITLE ) return m_url_title;
 
@@ -245,7 +245,7 @@
 //
 // 画面を消してレイアウトやりなおし & 再描画
 //
-void ArticleViewSearch::relayout()
+void ArticleViewSearch::relayout( const bool completely )
 {
 #ifdef _DEBUG
     std::cout << "ArticleViewSearch::relayout\n";
@@ -260,7 +260,7 @@
     if( m_searchmode == CORE::SEARCHMODE_ALLLOG ) comment <<  "検索対象:キャッシュ内の全ログ<br>";
     else if( m_searchmode == CORE::SEARCHMODE_TITLE ) comment << "検索サイト : "
                                                 + MISC::get_hostname( CONFIG::get_url_search_title() ) + "<br>";
-    else comment <<  "検索対象:" << DBTREE::board_name( m_url_board ) << "<br>";
+    else comment <<  "検索対象:" << MISC::html_escape( DBTREE::board_name( m_url_board ) ) << "<br>";
 
     if( get_bm() ) comment << "検索条件:しおり<br>";
 
@@ -288,9 +288,9 @@
 
                 // 板名表示
                 if( m_searchmode == CORE::SEARCHMODE_ALLLOG || m_searchmode == CORE::SEARCHMODE_TITLE  )
-                    comment << "[ <a href=\"" << DBTREE::url_subject( (*it).url_readcgi ) << "\">" << (*it).boardname << "</a> ] ";
+                    comment << "[ <a href=\"" << DBTREE::url_boardbase( (*it).url_readcgi ) << "\">" << MISC::html_escape( (*it).boardname ) << "</a> ] ";
 
-                comment << "<a href=\"" << (*it).url_readcgi << "\">" << MISC::html_escape( (*it).subject ) << "</a>";
+                comment << "<a href=\"" << (*it).url_readcgi << "\">" << (*it).subject << "</a>";
 
                 if( (*it).num ) comment << " ( " << (*it).num << " )";
 
diff -aurNZ jd-2.8.9-150226/src/article/articleviewsearch.h jd-2.8.9-a171004/src/article/articleviewsearch.h
--- jd-2.8.9-150226/src/article/articleviewsearch.h	2010-09-12 23:29:52.000000000 +0900
+++ jd-2.8.9-a171004/src/article/articleviewsearch.h	2016-03-16 00:29:42.486559000 +0900
@@ -35,17 +35,17 @@
 
         // SKELETON::View の関数のオーバロード
 
-        virtual const std::string url_for_copy(); // コピーやURLバー表示用のURL
+        virtual const std::string url_for_copy() const; // コピーやURLバー表示用のURL
         virtual const bool set_command( const std::string& command,
                                         const std::string& arg1 = std::string(),
                                         const std::string& arg2 = std::string()
             );
 
-        virtual const bool is_loading(){ return m_loading; }
+        virtual const bool is_loading() const { return m_loading; }
 
         virtual void focus_view();
         virtual void show_view();
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
         virtual void reload();
         virtual void stop();
 
diff -aurNZ jd-2.8.9-150226/src/article/drawareabase.cpp jd-2.8.9-a171004/src/article/drawareabase.cpp
--- jd-2.8.9-150226/src/article/drawareabase.cpp	2015-01-30 21:32:48.000000000 +0900
+++ jd-2.8.9-a171004/src/article/drawareabase.cpp	2017-03-28 18:55:59.196069000 +0900
@@ -11,6 +11,7 @@
 #include "font.h"
 #include "embeddedimage.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/miscmsg.h"
 #include "jdlib/jdregex.h"
@@ -519,13 +520,15 @@
 
     // 先頭のヘッダブロックから順に調べる
     LAYOUT* header = m_layout_tree->top_header();
-    while( header ){
+    LAYOUT* next_header = header ? header->next_header : NULL;
+    while( next_header ){
 
         // y が含まれているブロックを探す
-        if( header->rect->y >= y ) return header->res_number -1;
+        if( next_header->rect->y >= y ) return header->res_number;
 
         // 次のブロックへ
-        header = header->next_header;
+        header = next_header;
+        next_header = header->next_header;
     }
 
     return max_number();
@@ -859,7 +862,8 @@
     // フォント設定
     set_node_font( header );
 
-    CORE::get_css_manager()->set_size( &m_css_body, m_font->height );
+    const CORE::Css_Manager *cssmgr = CORE::get_css_manager();
+    cssmgr->set_size( &m_css_body, m_font->height );
 
     y += offset_y * m_font->br_size;
     y += m_css_body.padding_top;
@@ -874,7 +878,7 @@
 
         // (注) header は div ノードであり、クラス名は "res"
         // 詳しくは LayoutTree::create_layout_header() を参照せよ
-        CORE::get_css_manager()->set_size( header->css, m_font->height );
+        cssmgr->set_size( header->css, m_font->height );
 
         // div の位置と幅を計算
         // 高さは子ノードのレイアウトが全て済んでから計算
@@ -924,7 +928,7 @@
 
                     // div の位置と幅を計算
                     // 高さは違う div に切り替わった時に計算
-                    CORE::get_css_manager()->set_size( current_div->css, m_font->height );
+                    cssmgr->set_size( current_div->css, m_font->height );
                     x = header->rect->x + header->css->padding_left;
                     x += current_div->css->mrg_left;
                     y += current_div->css->mrg_top;
@@ -1422,13 +1426,20 @@
         if( pos_to >= byte_to ) break;
 
         // wrap 処理
-        x = 0;
-        if( div ) x = div->rect->x + div->css->padding_left;
+        x = div ? div->rect->x + div->css->padding_left : 0;
         y += br_size;
         br_size = m_font->br_size; // 次の行の改行位置をリセット
 
         pos_start = pos_to;
     }
+
+    // 次のノードが改行でなければ wrap するか確認する
+    if( layout->next_layout && layout->next_layout->type != DBTREE::NODE_BR
+            && is_wrapped( x, border, " " ) ){
+        x = div ? div->rect->x + div->css->padding_left : 0;
+        y += br_size;
+        br_size = m_font->br_size; // 次の行の改行位置をリセット
+    }
 }
 
 
@@ -1511,8 +1522,7 @@
     int i = pos_start;
     while( i < pos_to ){
 
-        int byte_tmp;
-        MISC::utf8toucs2( str + i, byte_tmp );
+        const int byte_tmp = MISC::utf8bytes( str + i );
 
         // 文字列に全角が含まれていたら全角モードで開始
         if( byte_tmp != 1 ) break;
@@ -1546,8 +1556,22 @@
     int width = 0;
     int width_wide = 0;
 
+    const int code = MISC::utf8tocp( utfstr, byte );
+
+    if( ! byte ){
+#ifdef _DEBUG
+        std::cout << "DrawAreaBase::get_width_of_one_char "
+                  << "invalid char " << (unsigned char)utfstr[ 0 ]
+                  << " " << (unsigned char)utfstr[ 1 ]
+                  << " " << (unsigned char)utfstr[ 2 ]
+                  << " " << (unsigned char)utfstr[ 3 ] << std::endl;
+#endif
+        byte = 1;
+        return 0;
+    }
+
     // キャッシュに無かったら幅を調べてキャッシュに登録
-    if( ! ARTICLE::get_width_of_char( utfstr, byte, pre_char, width, width_wide, mode ) ){
+    if( ! ARTICLE::get_width_of_char( code, pre_char, width, width_wide, mode ) ){
 
         const std::string tmpchar( utfstr, byte );
 
@@ -1609,26 +1633,26 @@
         // フォントが無い
         if( width_wide <= 0 ){
 
-            int byte_tmp;
-            const unsigned int code = MISC::utf8toucs2( tmpchar.c_str(), byte_tmp );
-
-            std::stringstream ss_err;
-            ss_err << "unknown font byte = " << byte_tmp << " ucs2 = " << code << " width = " << width;
-
 #ifdef _DEBUG
             std::cout << "DrawAreaBase::get_width_of_one_char "
                       << "byte = " << byte
-                      << " byte_tmp = " << byte_tmp
                       << " code = " << code
                       << " [" << tmpchar << "]\n";
 #endif
 
-            MISC::ERRMSG( ss_err.str() );
+            if( ( code < 0xE000 || code > 0xF8FF ) // 基本面私用領域ではない
+                && ( code < 0xF0000 || code > 0x10FFFF ) // 私用面ではない
+              ){
+                std::stringstream ss_err;
+                ss_err << "unknown font byte = " << byte << " ucs = " << code << " width = " << width;
+
+                MISC::ERRMSG( ss_err.str() );
+            }
 
-            ARTICLE::set_width_of_char( utfstr, byte, pre_char, -1, -1, mode );
+            ARTICLE::set_width_of_char( code, pre_char, -1, -1, mode );
             width = width_wide = 0;
         }
-        else ARTICLE::set_width_of_char( utfstr, byte, pre_char, width, width_wide, mode );
+        else ARTICLE::set_width_of_char( code, pre_char, width, width_wide, mode );
     }
 
     int ret = 0;
@@ -2202,7 +2226,9 @@
             if( layout->rect ){
                 const int x = layout->rect->x;
                 const int y = layout->rect->y - ci.pos_y;
-                const int color_text = get_colorid_text();
+                int color_text = get_colorid_text();
+                if( color_text == COLOR_CHAR && layout->div && layout->div->css->color >= 0 )
+                    color_text = layout->div->css->color;
                 m_gc->set_foreground( m_color[ color_text ] );
                 m_backscreen->draw_line( m_gc, x, y, x + layout->rect->width - 1, y );
             }
@@ -2508,7 +2534,8 @@
     if( color_text == COLOR_CHAR && layout->div && layout->div->css->color >= 0 ) color_text = layout->div->css->color;
 
     int color_back = get_colorid_back();
-    if( layout->div && layout->div->css->bg_color >= 0 ) color_back = layout->div->css->bg_color;
+    if( layout->node && layout->node->color_back != COLOR_NONE ) color_back = layout->node->color_back;
+    else if( layout->div && layout->div->css->bg_color >= 0 ) color_back = layout->div->css->bg_color;
     else if( layout->header && layout->header->css->bg_color >= 0 ) color_back = layout->header->css->bg_color;
 
     // 通常描画
@@ -2622,7 +2649,7 @@
         // 描画
         else{
 
-            Glib::RefPtr< Gdk::Pixbuf > pixbuf = layout->eimg->get_pixbuf();
+            Glib::RefPtr< Gdk::Pixbuf >& pixbuf = layout->eimg->get_pixbuf();
             if( pixbuf ){
 
                 const int s_top = MAX( 0, ci.upper - ( rect->y + 1 ) );
@@ -2755,13 +2782,20 @@
             }
         }
 
+        if( color >= int( m_color.size() ) || color_back >= int( m_color.size() ) ) init_color();
+
         if( width_line ){
 
             const int xx = x;
 
 #ifdef USE_PANGOLAYOUT  // Pango::Layout を使って文字を描画
 
-            m_pango_layout->set_text( Glib::ustring( node->text + pos_start, n_ustr ) );
+            // Glib::ustringのコンストラクタでchar*から変換するとUTF-8が
+            // 壊れている場合にインスタンスが生成されない
+            // (例外をキャッチしないとクラッシュする)ので
+            // std::stringからGlib::ustringに変換するコンストラクタを使う
+            //m_pango_layout->set_text( Glib::ustring( node->text + pos_start, n_ustr ) );
+            m_pango_layout->set_text( std::string( node->text + pos_start, n_byte ) );
             m_backscreen->draw_layout( m_gc,x, y, m_pango_layout, m_color[ color ], m_color[ color_back ] );
 
             if( node->bold ){
@@ -3451,11 +3485,13 @@
 
     if( list_query.size() == 0 ) return 0;
 
-    std::list< JDLIB::Regex > list_regex;
+    JDLIB::Regex regex;
+    std::list< JDLIB::RegexPattern > list_regex;
     const bool icase = true; // 大文字小文字区別しない
     const bool newline = true; // . に改行をマッチさせない
     const bool usemigemo = true; // migemo使用
-    const bool wchar = true;  // 全角半角の区別をしない
+    const bool wchar = false;  // 全角半角の区別をしない
+    const bool norm = true;  // Unicodeの互換文字を区別しない
 
 #ifdef _DEBUG
     std::cout << "ArticleViewBase::search size = " << list_query.size() << std::endl;
@@ -3464,10 +3500,8 @@
     std::list< std::string >::const_iterator it_query;
     for( it_query = list_query.begin(); it_query != list_query.end() ; ++it_query ){
 
-        const std::string &query = ( *it_query );
-
-        list_regex.push_back( JDLIB::Regex() );
-        list_regex.back().compile( query, icase, newline, usemigemo, wchar );
+        list_regex.push_back( JDLIB::RegexPattern() );
+        list_regex.back().set( *it_query, icase, newline, usemigemo, wchar, norm );
     }
 
     m_multi_selection.clear();
@@ -3504,7 +3538,7 @@
 
                         const size_t lng = strlen( tmplayout->text );
 
-                        if( buffer_lng + lng > SEARCH_BUFFER_SIZE ){
+                        if( buffer_lng + lng >= SEARCH_BUFFER_SIZE ){
 
                             MISC::ERRMSG( "DrawAreaBase::search : buffer overflow." );
                             break;
@@ -3530,11 +3564,10 @@
 
                     int min_offset = -1;
                     int lng = 0;
-                    std::list< JDLIB::Regex >::iterator it_regex;
+                    std::list< JDLIB::RegexPattern >::iterator it_regex;
                     for( it_regex = list_regex.begin(); it_regex != list_regex.end() ; ++it_regex ){
 
-                        JDLIB::Regex &regex = ( *it_regex );
-                        if( regex.exec( target, offset ) ){
+                        if( regex.match( *it_regex, target, offset ) ){
 
                             if( min_offset == -1 || regex.pos( 0 ) <= min_offset ){
                                 min_offset = regex.pos( 0 );
@@ -4109,15 +4142,15 @@
                 }
 
                 int byte_char_pointer;
-                const int ucs2_pointer = MISC::utf8toucs2( layout->text + pos, byte_char_pointer );
-                const int ucs2mode_pointer = MISC::get_ucs2mode( ucs2_pointer );
+                const int ucs_pointer = MISC::utf8tocp( layout->text + pos, byte_char_pointer );
+                const int ucstype_pointer = MISC::get_ucstype( ucs_pointer );
 #ifdef _DEBUG
-                std::cout << "ucs2 = " << std::hex << ucs2_pointer << std::dec
-                          << " mode = " << ucs2mode_pointer << " pos = " << pos << std::endl;
+                std::cout << "ucs = " << std::hex << ucs_pointer << std::dec
+                          << " type = " << ucstype_pointer << " pos = " << pos << std::endl;
 #endif
 
                 // 区切り文字をダブルクリックした
-                if( is_separate_char( ucs2_pointer ) ){
+                if( is_separate_char( ucs_pointer ) ){
                     caret_left.set( layout, pos );
                     caret_right.set( layout, pos + byte_char_pointer );
                     return true;
@@ -4129,24 +4162,21 @@
                 while( pos_tmp < pos ){
 
                     int byte_char;
-                    const int ucs2 = MISC::utf8toucs2( layout->text + pos_tmp, byte_char );
-                    const int ucs2mode = MISC::get_ucs2mode( ucs2 );
+                    const int ucs = MISC::utf8tocp( layout->text + pos_tmp, byte_char );
+                    const int ucstype = MISC::get_ucstype( ucs );
 
                     int byte_char_next;
-                    const int ucs2_next = MISC::utf8toucs2( layout->text + pos_tmp + byte_char, byte_char_next );
-                    const int ucs2mode_next = MISC::get_ucs2mode( ucs2_next );
+                    const int ucs_next = MISC::utf8tocp( layout->text + pos_tmp + byte_char, byte_char_next );
+                    const int ucstype_next = MISC::get_ucstype( ucs_next );
 
                     // 区切り文字が来たら左位置を移動する
-                    if( ucs2_next == '\0'
-
-                        || is_separate_char( ucs2 )
-
+                    if( ucs_next == '\0' || is_separate_char( ucs )
                         // 文字種が変わった
-                        || ( ucs2mode != ucs2mode_pointer && ucs2mode_next == ucs2mode_pointer )
+                        || ( ucstype != ucstype_pointer && ucstype_next == ucstype_pointer )
 
                         ) pos_left = pos_tmp + byte_char;
 
-                    pos_tmp += byte_char;
+                    pos_tmp += ( byte_char ? byte_char : 1 );
                 }
 
                 // 右位置を求める
@@ -4154,21 +4184,21 @@
                 while( pos_right < layout->lng_text ){
 
                     int byte_char;
-                    const int ucs2 = MISC::utf8toucs2( layout->text + pos_right, byte_char );
-                    const int ucs2mode = MISC::get_ucs2mode( ucs2 );
+                    const int ucs = MISC::utf8tocp( layout->text + pos_right, byte_char );
+                    const int ucstype = MISC::get_ucstype( ucs );
 
                     int byte_char_next;
-                    const int ucs2_next = MISC::utf8toucs2( layout->text + pos_right + byte_char, byte_char_next );
-                    const int ucs2mode_next = MISC::get_ucs2mode( ucs2_next );
+                    const int ucs_next = MISC::utf8tocp( layout->text + pos_right + byte_char, byte_char_next );
+                    const int ucstype_next = MISC::get_ucstype( ucs_next );
 
                     // 区切り文字が来たらbreak
-                    if( is_separate_char( ucs2 ) ) break;
+                    if( is_separate_char( ucs ) ) break;
 
-                    pos_right += byte_char;
+                    pos_right += ( byte_char ? byte_char : 1 );
 
                     // 文字種が変わった
-                    if( ucs2_next == '\0'
-                        || ( ucs2mode == ucs2mode_pointer && ucs2mode_next != ucs2mode_pointer )
+                    if( ucs_next == '\0'
+                        || ( ucstype == ucstype_pointer && ucstype_next != ucstype_pointer )
                         ) break;
                 }
 
diff -aurNZ jd-2.8.9-150226/src/article/drawareabase.h jd-2.8.9-a171004/src/article/drawareabase.h
--- jd-2.8.9-150226/src/article/drawareabase.h	2015-01-30 21:32:48.000000000 +0900
+++ jd-2.8.9-a171004/src/article/drawareabase.h	2016-12-13 15:29:37.996247000 +0900
@@ -330,7 +330,7 @@
 
         // リアライズしたか
         // Gtk::Widget::is_realized() はうまく動作しない
-        const bool is_drawarea_realized(){ return m_window; }
+        bool is_drawarea_realized(){ return static_cast<bool>(m_window); }
 
         // 文字色のID( colorid.h にある ID を指定)
         const int get_colorid_text() const{ return m_colorid_text; }
diff -aurNZ jd-2.8.9-150226/src/article/embeddedimage.cpp jd-2.8.9-a171004/src/article/embeddedimage.cpp
--- jd-2.8.9-150226/src/article/embeddedimage.cpp	2009-11-23 07:37:09.000000000 +0900
+++ jd-2.8.9-a171004/src/article/embeddedimage.cpp	2015-05-20 17:29:46.375290000 +0900
@@ -82,8 +82,6 @@
 #ifdef _DEBUG    
     std::cout << "EmbeddedImage::stop" << std::endl;
 #endif 
-    if( m_imgloader )
-        m_imgloader->request_stop();
 }
 
 
@@ -135,8 +133,8 @@
 
     if( m_img->get_type() == DBIMG::T_BMP ) pixbufonly = false; // BMP の場合 pixbufonly = true にすると真っ黒になる
     
-    m_imgloader = JDLIB::ImgLoader::get_loader( m_img->get_cache_path() );
-    Glib::RefPtr<Gdk::Pixbuf> pixbuf = m_imgloader->get_pixbuf( pixbufonly );
+    Glib::RefPtr<JDLIB::ImgLoader> imgloader = JDLIB::ImgLoader::get_loader( m_img->get_cache_path() );
+    Glib::RefPtr<Gdk::Pixbuf> pixbuf = imgloader->get_pixbuf( pixbufonly );
     if( pixbuf ){
         Gdk::InterpType interptype = Gdk::INTERP_NEAREST;
         if( CONFIG::get_imgemb_interp() == 1 ) interptype = Gdk::INTERP_BILINEAR;
@@ -144,7 +142,7 @@
 
         m_pixbuf = pixbuf->scale_simple( width, height, interptype );
     }
-    m_imgloader.clear();
+    imgloader.clear();
 
     // メインスレッドにリサイズが終わったことを知らせて
     // メインスレッドがpthread_join()を呼び出す
diff -aurNZ jd-2.8.9-150226/src/article/embeddedimage.h jd-2.8.9-a171004/src/article/embeddedimage.h
--- jd-2.8.9-150226/src/article/embeddedimage.h	2009-11-07 04:01:19.000000000 +0900
+++ jd-2.8.9-a171004/src/article/embeddedimage.h	2015-06-30 04:27:27.965608000 +0900
@@ -28,14 +28,12 @@
         JDLIB::ConstPtr< DBIMG::Img > m_img;
         JDLIB::Thread m_thread;
 
-        Glib::RefPtr< JDLIB::ImgLoader > m_imgloader;
-
       public:
 
         EmbeddedImage( const std::string& url );
         ~EmbeddedImage();
 
-        Glib::RefPtr< Gdk::Pixbuf > get_pixbuf(){ return m_pixbuf; }
+        Glib::RefPtr< Gdk::Pixbuf >& get_pixbuf(){ return m_pixbuf; }
 
         void show();
         void resize_thread();
diff -aurNZ jd-2.8.9-150226/src/article/font.cpp jd-2.8.9-a171004/src/article/font.cpp
--- jd-2.8.9-150226/src/article/font.cpp	2013-01-13 18:32:27.000000000 +0900
+++ jd-2.8.9-a171004/src/article/font.cpp	2017-03-27 16:37:26.269520000 +0900
@@ -5,8 +5,6 @@
 
 #include "font.h"
 
-#include "jdlib/miscutil.h"
-
 #include "fontid.h"
 #include "config/globalconf.h"
 
@@ -29,7 +27,7 @@
 
 enum
 {
-    UCS2_MAX = 65536
+    NUM_CHAR_MAX = 0x20000 // フォントの幅を記録する文字数(基本面+1面)
 };
 
 
@@ -45,14 +43,13 @@
 
         if( width_of_char[ i ]  ){
 
-            for( int j = 0; j < UCS2_MAX; ++j ){
+            for( int j = 0; j < NUM_CHAR_MAX; ++j ){
                 
-                if( width_of_char[ i ][ j ].width ) delete width_of_char[ i ][ j ].width;
+                if( width_of_char[ i ][ j ].width ) free( width_of_char[ i ][ j ].width );
             }
-            delete width_of_char[ i ];
+            free( width_of_char[ i ] );
+            width_of_char[ i ] = NULL;
         }
-
-        width_of_char[ i ] = NULL;
     }
 }
 
@@ -61,56 +58,70 @@
 //
 // 登録された文字の幅を返す関数
 //
-// utfstr : 入力文字 (UTF-8)
-// byte   : 長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を入れて返す
+// code   : 入力文字 (コードポイント)
 // pre_char : ひとつ前の文字 ( 前の文字が全角の場合は 0 )
 // width  : 半角モードでの幅
 // width_wide : 全角モードでの幅
 // mode   : fontid.h で定義されているフォントのID
 // 戻り値 : 登録されていればtrue
 // 
-const bool ARTICLE::get_width_of_char( const char* utfstr, int& byte, const char pre_char, int& width, int& width_wide, const int mode )
+const bool ARTICLE::get_width_of_char( const int code, const char pre_char, int& width, int& width_wide, const int mode )
 {
-    byte = 0;
     width = 0;
     width_wide = 0;
 
     if( ! width_of_char[ mode ] ){
-        const int size = sizeof( WIDTH_DATA ) * UCS2_MAX;
+        const int size = sizeof( WIDTH_DATA ) * NUM_CHAR_MAX;
 
         width_of_char[ mode ] = ( WIDTH_DATA* ) malloc( size );
         memset( width_of_char[ mode ], 0, size );
+
+        // 合成文字の初期化
+        for( int i = 0x300; i <= 0x36f; i++ ) // Combining Diacritical Marks
+            width_of_char[ mode ][ i ].width_wide = -1;
+        for( int i = 0x180b; i <= 0x180d; i++ ) // Mongolian Free Variation Selector
+            width_of_char[ mode ][ i ].width_wide = -1;
+        for( int i = 0x200b; i <= 0x200f; i++ ) // ZWSP,ZWNJ,ZWJ,LRM,RLM
+            width_of_char[ mode ][ i ].width_wide = -1;
+        for( int i = 0x202a; i <= 0x202e; i++ ) // LRE,RLE,PDF,LRO,RLO
+            width_of_char[ mode ][ i ].width_wide = -1;
+        for( int i = 0x20d0; i <= 0x20ff; i++ ) // Combining Diacritical Marks for Symbols
+            width_of_char[ mode ][ i ].width_wide = -1;
+        for( int i = 0x3099; i <= 0x309a; i++ ) // COMBINING KATAKANA-HIRAGANA (SEMI-)VOICED SOUND MARK
+            width_of_char[ mode ][ i ].width_wide = -1;
+        for( int i = 0xfe00; i <= 0xfe0f; i++ ) // VS1-VS16
+            width_of_char[ mode ][ i ].width_wide = -1;
+        width_of_char[ mode ][ 0xfeff ].width_wide = -1; // ZERO WIDTH NO-BREAK SPACE
     }
 
-    const int ucs2 = MISC::utf8toucs2( utfstr, byte );
-    if( byte && ucs2 < UCS2_MAX ){
+    if( code > 0 && code < NUM_CHAR_MAX ){
 
         // 全角モードの幅
-        width_wide = width_of_char[ mode ][ ucs2 ].width_wide;
+        width_wide = width_of_char[ mode ][ code ].width_wide;
 
         // 半角モードの幅
         width = width_wide;
 
         // 厳密に求める場合
-        if( byte == 1 && strict_of_char ){
+        if( code < 128 && strict_of_char ){
 
-            if( ! width_of_char[ mode ][ ucs2 ].width ){
+            if( ! width_of_char[ mode ][ code ].width ){
                 const int size = sizeof( unsigned int ) * 128;
 
-                width_of_char[ mode ][ ucs2 ].width = ( unsigned int* ) malloc( size );
-                memset( width_of_char[ mode ][ ucs2 ].width, 0, size );
+                width_of_char[ mode ][ code ].width = ( unsigned int* ) malloc( size );
+                memset( width_of_char[ mode ][ code ].width, 0, size );
             }
 
             const int pre_char_num = ( const int ) pre_char;
-            if( pre_char_num < 128 ) width = width_of_char[ mode ][ ucs2 ].width[ pre_char_num ];
+            if( pre_char_num < 128 ) width = width_of_char[ mode ][ code ].width[ pre_char_num ];
         }
     }
 
-    if( width && width_wide ) return true;
-    else if( width == -1 ){ // フォント幅の取得に失敗した場合
+    if( width == -1 ){ // フォント幅の取得に失敗した場合
         width = width_wide = 0;
         return true;
     }
+    else if( width && width_wide ) return true;
 
     return false;
 }
@@ -122,19 +133,17 @@
 //
 // width == -1 はフォント幅の取得に失敗した場合
 //
-void ARTICLE::set_width_of_char( const char* utfstr, int& byte, const char pre_char, const int width, const int width_wide, const int mode )
+void ARTICLE::set_width_of_char( const int code, const char pre_char, const int width, const int width_wide, const int mode )
 {    
-    const int ucs2 = MISC::utf8toucs2( utfstr, byte );
-    if( ! byte ) return;
-    if( ucs2 >= UCS2_MAX ) return;
+    if( code <= 0 || code >= NUM_CHAR_MAX ) return;
 
     // 半角モードの幅を厳密に求める場合
-    if( byte == 1 && strict_of_char ){
+    if( code < 128 && strict_of_char ){
 
         const int pre_char_num = ( const int ) pre_char;
-        if( pre_char_num < 128 ) width_of_char[ mode ][ ucs2 ].width[ pre_char_num ] = width;
+        if( pre_char_num < 128 ) width_of_char[ mode ][ code ].width[ pre_char_num ] = width;
     }
 
     // 全角モードの幅
-    width_of_char[ mode ][ ucs2 ].width_wide = width_wide;
+    width_of_char[ mode ][ code ].width_wide = width_wide;
 }
diff -aurNZ jd-2.8.9-150226/src/article/font.h jd-2.8.9-a171004/src/article/font.h
--- jd-2.8.9-150226/src/article/font.h	2009-05-13 00:58:36.000000000 +0900
+++ jd-2.8.9-a171004/src/article/font.h	2016-01-01 22:50:30.195269000 +0900
@@ -10,18 +10,17 @@
     void init_font();
 
     // 登録された文字の幅を返す関数
-    // utfstr : 入力文字 (UTF-8)
-    // byte   : 長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を入れて返す
+    // code   : 入力文字 (コードポイント)
     // pre_char : ひとつ前の文字 ( 前の文字が全角の場合は 0 )
     // width  : 半角モードでの幅
     // width_wide : 全角モードでの幅
     // mode   : fontid.h で定義されているフォントのID
     // 戻り値 : 登録されていればtrue
-    const bool get_width_of_char( const char* utfstr, int& byte, const char pre_char, int& width, int& width_wide, const int mode );
+    const bool get_width_of_char( const int code, const char pre_char, int& width, int& width_wide, const int mode );
 
     // 文字幅を登録する関数
     // width == -1 はフォント幅の取得に失敗した場合
-    void set_width_of_char( const char* utfstr, int& byte, const char pre_char, const int width, const int width_wide, const int mode );
+    void set_width_of_char( const int code, const char pre_char, const int width, const int width_wide, const int mode );
 }
 
 #endif
diff -aurNZ jd-2.8.9-150226/src/article/layouttree.cpp jd-2.8.9-a171004/src/article/layouttree.cpp
--- jd-2.8.9-150226/src/article/layouttree.cpp	2015-01-25 15:54:29.000000000 +0900
+++ jd-2.8.9-a171004/src/article/layouttree.cpp	2016-01-08 03:29:22.999065000 +0900
@@ -20,7 +20,7 @@
 
 enum
 {
-    SIZE_OF_HEAP = 256 * 1024,
+    SIZE_OF_HEAP = 512 * 1024,
 
     STEP_ID = 10,
     STEP_SEPARATOR = 1,
@@ -311,6 +311,8 @@
     // 連結モード
     // 本文ブロックだけ追加
     if( joint ){
+        int classid = CORE::get_css_manager()->get_classid( "mes" );
+        create_layout_div( classid );
         create_layout_br( m_last_dom_attr & CORE::DOMATTR_NOBR );
         append_block( headinfo->block[ DBTREE::BLOCK_MES ], res_number, NULL, m_last_dom_attr );
     }
@@ -398,6 +400,10 @@
                 tmplayout = create_layout_text( tmpnode->text, &tmpnode->color_text, tmpnode->bold );
                 break;
 
+            case DBTREE::NODE_SP: // 半角スペース
+                tmplayout = create_layout_text( " ", &tmpnode->color_text, tmpnode->bold );
+                break;
+
             case DBTREE::NODE_LINK:
                 tmplayout = create_layout_link( tmpnode->text, tmpnode->linkinfo->link,
                                                 &tmpnode->color_text, tmpnode->bold );
@@ -444,7 +450,8 @@
                 break;
 
             case DBTREE::NODE_HTAB: // 水平タブ
-                tmplayout = create_layout_hspace( tmpnode->type );
+                if( m_show_multispace ) tmplayout = create_layout_hspace( tmpnode->type );
+                else tmplayout = create_layout_text( " ", &tmpnode->color_text, tmpnode->bold );
                 break;
         }
 
diff -aurNZ jd-2.8.9-150226/src/article/preference.cpp jd-2.8.9-a171004/src/article/preference.cpp
--- jd-2.8.9-150226/src/article/preference.cpp	2011-07-30 23:19:45.000000000 +0900
+++ jd-2.8.9-a171004/src/article/preference.cpp	2017-03-28 22:42:31.363860000 +0900
@@ -4,6 +4,7 @@
 
 #include "dbtree/interface.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/misctime.h"
 
@@ -13,6 +14,7 @@
 
 #include "cache.h"
 #include "command.h"
+#include "global.h"
 
 #include <string>
 #include <list>
@@ -21,7 +23,7 @@
 
 Preferences::Preferences( Gtk::Window* parent, const std::string& url, const std::string command )
     : SKELETON::PrefDiag( parent, url )
-    ,m_label_name( false, "スレタイトル : ", DBTREE::article_subject( get_url() ) )
+    ,m_label_name( false, "スレタイトル : ", MISC::to_plain( DBTREE::article_subject( get_url() ) ) )
     ,m_label_url( false, "スレのURL : ", DBTREE:: url_readcgi( get_url(),0,0 ) )
     ,m_label_url_dat( false, "DATファイルのURL : ", DBTREE:: url_dat( get_url() ) )
     ,m_label_cache( false, "ローカルキャッシュパス : ", std::string() )
@@ -29,6 +31,8 @@
     ,m_check_transpabone( "透明あぼ〜ん" )
     ,m_check_chainabone( "連鎖あぼ〜ん" )
     ,m_check_ageabone( "sage以外をあぼ〜ん" )
+    ,m_check_defnameabone( "デフォルト名無しをあぼ〜ん" )
+    ,m_check_noidabone( "ID無しをあぼ〜ん" )
     ,m_check_boardabone( "板レベルでのあぼ〜んを有効にする" )
     ,m_check_globalabone( "全体レベルでのあぼ〜んを有効にする" )
     ,m_label_since( false, "スレ立て日時 : ", std::string() )
@@ -72,15 +76,49 @@
     m_hbox_write.pack_start( m_label_write );
     m_hbox_write.pack_start( m_bt_clear_post_history, Gtk::PACK_SHRINK );    
 
+    m_label_charset.set_text( "エンコーディング:" );
+#if GTKMM_CHECK_VERSION(2,24,0)
+    m_combo_charset.append( MISC::charcode_to_cstr( CHARCODE_UTF8 ) );
+    m_combo_charset.append( MISC::charcode_to_cstr( CHARCODE_SJIS ) );
+    m_combo_charset.append( MISC::charcode_to_cstr( CHARCODE_EUCJP ) );
+    m_combo_charset.set_active_text( MISC::charcode_to_cstr( DBTREE::article_charcode( get_url() ) ) );
+#else
+    std::list< Glib::ustring > list_charset;
+    list_charset.push_back( MISC::charcode_to_cstr( CHARCODE_UTF8 ) );
+    list_charset.push_back( MISC::charcode_to_cstr( CHARCODE_SJIS ) );
+    list_charset.push_back( MISC::charcode_to_cstr( CHARCODE_EUCJP ) );
+    m_combo_charset.set_popdown_strings( list_charset );
+    m_combo_charset.get_entry()->set_text( MISC::charcode_to_cstr( DBTREE::article_charcode( get_url() ) ) );
+    m_combo_charset.get_entry()->set_editable( false );
+    m_combo_charset.set_value_in_list( false );
+#endif
+
+    m_hbox_since.pack_start( m_label_since );
+    m_hbox_since.pack_start( m_label_charset, Gtk::PACK_SHRINK );
+    m_hbox_since.pack_start( m_combo_charset, Gtk::PACK_SHRINK );
+
+
+    // 最大レス数
+    const int max_res = DBTREE::article_number_max( get_url() );
+    m_label_maxres.set_text( "最大レス数 (0 : 未設定):" );
+    m_spin_maxres.set_range( 0, MAX_RESNUMBER );
+    m_spin_maxres.set_increments( 1, 1 );
+    m_spin_maxres.set_value( max_res );
+    m_spin_maxres.set_sensitive( true );
+
+    m_hbox_size.pack_start( m_label_size );
+    m_hbox_size.pack_start( m_label_maxres, Gtk::PACK_SHRINK );
+    m_hbox_size.pack_start( m_spin_maxres, Gtk::PACK_SHRINK );
+
     m_vbox_info.set_border_width( 16 );
     m_vbox_info.set_spacing( 8 );
     m_vbox_info.pack_start( m_label_name, Gtk::PACK_SHRINK );
     m_vbox_info.pack_start( m_label_url, Gtk::PACK_SHRINK );
     m_vbox_info.pack_start( m_label_url_dat, Gtk::PACK_SHRINK );
     m_vbox_info.pack_start( m_label_cache, Gtk::PACK_SHRINK );
-    m_vbox_info.pack_start( m_label_size, Gtk::PACK_SHRINK );
+    m_vbox_info.pack_start( m_hbox_size, Gtk::PACK_SHRINK );
 
-    m_vbox_info.pack_start( m_label_since, Gtk::PACK_SHRINK );
+    m_vbox_info.pack_start( m_hbox_since, Gtk::PACK_SHRINK );
     m_vbox_info.pack_start( m_hbox_modified, Gtk::PACK_SHRINK );
     m_vbox_info.pack_start( m_hbox_write, Gtk::PACK_SHRINK );
 
@@ -103,6 +141,12 @@
     // ageあぼーん
     m_check_ageabone.set_active( DBTREE::get_abone_age( get_url() ) );
 
+    // デフォルト名無しあぼーん
+    m_check_defnameabone.set_active( DBTREE::get_abone_default_name( get_url() ) );
+
+    // ID無しあぼーん
+    m_check_noidabone.set_active( DBTREE::get_abone_noid( get_url() ) );
+
     // 板レベルあぼーん
     m_check_boardabone.set_active( DBTREE::get_abone_board( get_url() ) );
 
@@ -115,6 +159,8 @@
     m_vbox_abone.pack_start( m_check_transpabone, Gtk::PACK_SHRINK );
     m_vbox_abone.pack_start( m_check_chainabone, Gtk::PACK_SHRINK );
     m_vbox_abone.pack_start( m_check_ageabone, Gtk::PACK_SHRINK );
+    m_vbox_abone.pack_start( m_check_defnameabone, Gtk::PACK_SHRINK );
+    m_vbox_abone.pack_start( m_check_noidabone, Gtk::PACK_SHRINK );
     m_vbox_abone.pack_start( m_check_boardabone, Gtk::PACK_SHRINK );
     m_vbox_abone.pack_start( m_check_globalabone, Gtk::PACK_SHRINK );
 
@@ -205,7 +251,7 @@
     m_notebook.append_page( m_notebook_abone, "あぼ〜ん設定" );
 
     get_vbox()->pack_start( m_notebook );
-    set_title( "「" + DBTREE::article_subject( get_url() ) + "」のプロパティ" );
+    set_title( "「" + MISC::to_plain( DBTREE::article_modified_subject( get_url() ) ) + "」のプロパティ" );
     resize( 600, 400 );
     show_all_children();
 
@@ -246,8 +292,26 @@
 
     DBTREE::reset_abone( get_url(), list_id, list_name, list_word, list_regex, vec_abone_res
                          , m_check_transpabone.get_active(), m_check_chainabone.get_active(), m_check_ageabone.get_active(),
+                         m_check_defnameabone.get_active(), m_check_noidabone.get_active(),
                          m_check_boardabone.get_active(), m_check_globalabone.get_active() );
 
+    //最大レス数
+    DBTREE::article_set_number_max( get_url(), m_spin_maxres.get_value_as_int() );
+
+    // charset
+#if GTKMM_CHECK_VERSION(2,24,0)
+    std::string tmpcharset = m_combo_charset.get_active_text();
+#else
+    std::string tmpcharset = m_combo_charset.get_entry()->get_text();
+#endif
+    CharCode tmpcharcode = MISC::charcode_from_cstr( tmpcharset.c_str() );
+    if( tmpcharcode != DBTREE::article_charcode( get_url() ) ){
+        // charcodeを更新
+        DBTREE::article_set_charcode( get_url(), tmpcharcode );
+        // Viewが開かれていない場合があるのでここでNodeTreeを削除する
+        DBTREE::article_clear_nodetree( get_url() );
+    }
+
     // viewの再レイアウト
     CORE::core_set_command( "relayout_article", get_url() );
 }
@@ -278,5 +342,5 @@
     CORE::core_set_command( "redraw_article" );
 
     // BoardViewの行を更新
-    CORE::core_set_command( "update_board_item", DBTREE::url_subject( get_url() ), DBTREE::article_id( get_url() ) );
+    CORE::core_set_command( "update_board_item", DBTREE::url_boardbase( get_url() ), DBTREE::article_id( get_url() ) );
 }
diff -aurNZ jd-2.8.9-150226/src/article/preference.h jd-2.8.9-a171004/src/article/preference.h
--- jd-2.8.9-150226/src/article/preference.h	2011-07-30 23:19:45.000000000 +0900
+++ jd-2.8.9-a171004/src/article/preference.h	2017-03-28 14:13:39.352328000 +0900
@@ -3,9 +3,11 @@
 #ifndef _ARTICLE_PREFERENCES_H
 #define _ARTICLE_PREFERENCES_H
 
+#include "gtkmmversion.h"
 #include "skeleton/prefdiag.h"
 #include "skeleton/editview.h"
 #include "skeleton/label_entry.h"
+#include "skeleton/spinbutton.h"
 
 namespace ARTICLE
 {
@@ -21,6 +23,20 @@
         SKELETON::LabelEntry m_label_cache;
         SKELETON::LabelEntry m_label_size;
 
+        Gtk::HBox m_hbox_size;
+
+        // 最大レス数
+        Gtk::Label m_label_maxres;
+        SKELETON::SpinButton m_spin_maxres;
+
+        // エンコーディング
+        Gtk::Label m_label_charset;
+#if GTKMM_CHECK_VERSION(2,24,0)
+        Gtk::ComboBoxText m_combo_charset;
+#else
+        Gtk::Combo m_combo_charset;
+#endif
+
         // あぼーん
         Gtk::VBox m_vbox_abone;
         Gtk::Notebook m_notebook_abone;
@@ -39,12 +55,19 @@
         // ageあぼーん
         Gtk::CheckButton m_check_ageabone;
 
+        // ageあぼーん
+        Gtk::CheckButton m_check_defnameabone;
+
+        // ageあぼーん
+        Gtk::CheckButton m_check_noidabone;
+
         // 板レベルでのあぼーん
         Gtk::CheckButton m_check_boardabone;
 
         // 全体レベルでのあぼーん
         Gtk::CheckButton m_check_globalabone;
 
+        Gtk::HBox m_hbox_since;
         SKELETON::LabelEntry m_label_since;
 
         // 最終更新日時
diff -aurNZ jd-2.8.9-150226/src/article/toolbar.cpp jd-2.8.9-a171004/src/article/toolbar.cpp
--- jd-2.8.9-150226/src/article/toolbar.cpp	2011-05-31 00:08:41.000000000 +0900
+++ jd-2.8.9-a171004/src/article/toolbar.cpp	2017-04-05 21:37:55.668427000 +0900
@@ -50,6 +50,10 @@
 }
         
 
+ArticleToolBar::~ArticleToolBar()
+{}
+
+
 //
 // タブが切り替わった時にDragableNoteBook::set_current_toolbar()から呼び出される( Viewの情報を取得する )
 //
diff -aurNZ jd-2.8.9-150226/src/article/toolbar.h jd-2.8.9-a171004/src/article/toolbar.h
--- jd-2.8.9-150226/src/article/toolbar.h	2011-05-31 00:08:41.000000000 +0900
+++ jd-2.8.9-a171004/src/article/toolbar.h	2017-04-05 21:37:25.568427000 +0900
@@ -33,7 +33,7 @@
       public:
 
         ArticleToolBar(); 
-        virtual ~ArticleToolBar(){}
+        virtual ~ArticleToolBar();
 
         // タブが切り替わった時に呼び出される( Viewの情報を取得する )
         virtual void set_view( SKELETON::View * view );
diff -aurNZ jd-2.8.9-150226/src/article/toolbarsearch.cpp jd-2.8.9-a171004/src/article/toolbarsearch.cpp
--- jd-2.8.9-150226/src/article/toolbarsearch.cpp	2011-06-03 00:05:53.000000000 +0900
+++ jd-2.8.9-a171004/src/article/toolbarsearch.cpp	2017-04-05 21:36:23.408428000 +0900
@@ -37,6 +37,10 @@
 }
 
 
+SearchToolBar::~SearchToolBar()
+{}
+
+
 //
 // ボタンのパッキング
 //
diff -aurNZ jd-2.8.9-150226/src/article/toolbarsearch.h jd-2.8.9-a171004/src/article/toolbarsearch.h
--- jd-2.8.9-150226/src/article/toolbarsearch.h	2010-05-25 23:56:48.000000000 +0900
+++ jd-2.8.9-a171004/src/article/toolbarsearch.h	2017-04-05 21:35:55.332429000 +0900
@@ -26,7 +26,7 @@
       public:
 
         SearchToolBar();
-        virtual ~SearchToolBar(){}
+        virtual ~SearchToolBar();
 
         // タブが切り替わった時に呼び出される( Viewの情報を取得する )
         virtual void set_view( SKELETON::View * view );
diff -aurNZ jd-2.8.9-150226/src/article/toolbarsimple.cpp jd-2.8.9-a171004/src/article/toolbarsimple.cpp
--- jd-2.8.9-150226/src/article/toolbarsimple.cpp	2010-05-25 23:56:48.000000000 +0900
+++ jd-2.8.9-a171004/src/article/toolbarsimple.cpp	2017-04-05 21:37:05.084428000 +0900
@@ -24,6 +24,10 @@
 }
 
 
+ArticleToolBarSimple::~ArticleToolBarSimple()
+{}
+
+
 //
 // ボタンのパッキング
 //
diff -aurNZ jd-2.8.9-150226/src/article/toolbarsimple.h jd-2.8.9-a171004/src/article/toolbarsimple.h
--- jd-2.8.9-150226/src/article/toolbarsimple.h	2010-05-25 23:56:48.000000000 +0900
+++ jd-2.8.9-a171004/src/article/toolbarsimple.h	2017-04-05 21:36:38.948428000 +0900
@@ -14,7 +14,7 @@
       public:
 
         ArticleToolBarSimple();
-        virtual ~ArticleToolBarSimple(){}
+        virtual ~ArticleToolBarSimple();
 
       protected:
 
diff -aurNZ jd-2.8.9-150226/src/bbslist/Makefile.am jd-2.8.9-a171004/src/bbslist/Makefile.am
--- jd-2.8.9-150226/src/bbslist/Makefile.am	2013-01-06 00:44:26.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/Makefile.am	2017-04-11 13:25:10.018102000 +0900
@@ -15,7 +15,7 @@
 
 noinst_HEADERS = \
 	bbslistadmin.h \
-	bbslistviewbase.h
+	bbslistviewbase.h \
 	bbslistview.h \
 	favoriteview.h \
 	selectlistview.h \
diff -aurNZ jd-2.8.9-150226/src/bbslist/addetcdialog.cpp jd-2.8.9-a171004/src/bbslist/addetcdialog.cpp
--- jd-2.8.9-150226/src/bbslist/addetcdialog.cpp	2010-04-08 23:35:22.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/addetcdialog.cpp	2017-04-05 21:35:28.704429000 +0900
@@ -49,3 +49,8 @@
 
     show_all_children();
 }
+
+
+AddEtcDialog::~AddEtcDialog()
+{}
+
diff -aurNZ jd-2.8.9-150226/src/bbslist/addetcdialog.h jd-2.8.9-a171004/src/bbslist/addetcdialog.h
--- jd-2.8.9-150226/src/bbslist/addetcdialog.h	2008-11-29 21:57:57.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/addetcdialog.h	2017-04-05 21:34:58.732430000 +0900
@@ -24,6 +24,7 @@
       public:
 
         AddEtcDialog( const bool move, const std::string& url, const std::string& _name, const std::string& _id, const std::string& _passwd );
+        virtual ~AddEtcDialog();
 
         const std::string get_name(){ return m_entry_name.get_text(); }
         const std::string get_url(){ return m_entry_url.get_text(); }
diff -aurNZ jd-2.8.9-150226/src/bbslist/bbslistadmin.cpp jd-2.8.9-a171004/src/bbslist/bbslistadmin.cpp
--- jd-2.8.9-150226/src/bbslist/bbslistadmin.cpp	2013-07-28 15:49:29.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/bbslistadmin.cpp	2016-03-05 16:07:23.868266000 +0900
@@ -247,9 +247,12 @@
 
         // 板のアイコン表示を更新
         else if( command.command  == "toggle_boardicon" ) view->set_command( "toggle_boardicon", command.arg1 );
+    }
 
-        // URLを選択
-        else if( command.command  == "select_item" ) view->set_command( "select_item", command.arg1 );
+    // URLを選択
+    if( command.command == "select_item" ){
+        view = get_current_view();
+        if( view ) view->set_command( "select_item", command.arg1 );
     }
 }
 
diff -aurNZ jd-2.8.9-150226/src/bbslist/bbslistviewbase.cpp jd-2.8.9-a171004/src/bbslist/bbslistviewbase.cpp
--- jd-2.8.9-150226/src/bbslist/bbslistviewbase.cpp	2014-04-13 12:41:07.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/bbslistviewbase.cpp	2016-03-05 17:14:03.632205000 +0900
@@ -644,7 +644,7 @@
 //
 // 色やフォントなどの変更
 //
-void BBSListViewBase::relayout()
+void BBSListViewBase::relayout( const bool completely )
 {
     m_treeview.init_color( COLOR_CHAR_BBS, COLOR_BACK_BBS, COLOR_BACK_BBS_EVEN );
     m_treeview.init_font( CONFIG::get_fontname( FONT_BBS ) );
@@ -826,7 +826,7 @@
 
         case CONTROL::Left:
 
-            if( row = m_treeview.get_row( path ) ){
+            if( ( row = m_treeview.get_row( path ) ) ){
 
                 if( ( path2type( path ) != TYPE_DIR || ! m_treeview.row_expanded( path ) ) && row.parent() ){
                     path = GET_PATH( row.parent() );
@@ -1159,23 +1159,23 @@
         const int mrg = 16; // アイコンの横幅。計算するのが面倒だったのでとりあえず
 
         Gtk::TreeModel::Row row = m_treeview.get_row( path );
-        Glib::ustring subject = row[ m_columns.m_name ];
-        Glib::ustring url = row[ m_columns.m_url ];
+        const Glib::ustring ustr_subject = row[ m_columns.m_name ];
+        const Glib::ustring ustr_url = row[ m_columns.m_url ];
         int type = row[ m_columns.m_type ];
 
-        m_treeview.reset_pre_popupurl( url );
+        m_treeview.reset_pre_popupurl( ustr_url.raw() );
 
         // 画像ポップアップ
         if( type == TYPE_IMAGE ){
 
             m_treeview.hide_tooltip();
 
-            if( DBIMG::get_type_ext( url ) != DBIMG::T_UNKNOWN && DBIMG::get_code( url ) != HTTP_INIT ){
+            if( DBIMG::get_type_ext( ustr_url.raw() ) != DBIMG::T_UNKNOWN && DBIMG::get_code( ustr_url.raw() ) != HTTP_INIT ){
 
-                if( m_treeview.pre_popup_url() != url ){
+                if( m_treeview.pre_popup_url() != ustr_url.raw() ){
 
-                    SKELETON::View* view = CORE::ViewFactory( CORE::VIEW_IMAGEPOPUP,  url );
-                    m_treeview.show_popup( url, view );
+                    SKELETON::View* view = CORE::ViewFactory( CORE::VIEW_IMAGEPOPUP, ustr_url.raw() );
+                    m_treeview.show_popup( ustr_url.raw(), view );
                 }
             }
             else m_treeview.hide_popup();
@@ -1189,7 +1189,7 @@
             Gdk::Rectangle rect;
             m_treeview.get_cell_area( path, *column, rect );
             m_treeview.set_tooltip_min_width( rect.get_width() - mrg );
-            m_treeview.set_str_tooltip( subject );
+            m_treeview.set_str_tooltip( ustr_subject.raw() );
         }
     }
     else{
@@ -1471,7 +1471,7 @@
         }
 
         // http が無ければ付ける
-        if( url.find( "http://" ) != 0 ) url = "http://" + url;
+        if( url.find( "://" ) == std::string::npos ) url = "http://" + url;
 
         // .htmlを取り除く
         JDLIB::Regex regex;
@@ -1490,7 +1490,7 @@
         url += "/";
 
         // boardid 取得
-        if( ! regex.exec( "(http://.*)/([^/]*)/$" , url, offset, icase, newline, usemigemo, wchar ) ){
+        if( ! regex.exec( "(https?://.*)/([^/]*)/$" , url, offset, icase, newline, usemigemo, wchar ) ){
             SKELETON::MsgDiag mdiag( get_parent_win(), "アドレスが不正な形式になっています", false, Gtk::MESSAGE_ERROR );
             mdiag.run();
             mdiag.hide();
@@ -1607,9 +1607,21 @@
     if( m_path_selected.empty() ) return;
 
     const std::string url = path2url( m_path_selected );
-    const std::string name = path2name( m_path_selected );
+    std::string name;
 
-    MISC::CopyClipboard( name + '\n' + url );
+    int type = path2type( m_path_selected );
+    switch( type ){
+        case TYPE_THREAD:
+        case TYPE_THREAD_UPDATE:
+        case TYPE_THREAD_OLD:
+            name = MISC::to_plain( DBTREE::article_subject( url ) );
+            break;
+
+        default:
+            name = path2name( m_path_selected );
+    }
+
+    MISC::CopyClipboard( name + '\n' + url + '\n' );
 }
 
 
@@ -1712,7 +1724,7 @@
 
         if( type == TYPE_THREAD || type == TYPE_THREAD_UPDATE ) CORE::get_checkupdate_manager()->push_back( DBTREE::url_dat( url ), open );
         else if( CONFIG::get_check_update_board() && ( type == TYPE_BOARD || type == TYPE_BOARD_UPDATE ) )
-            CORE::get_checkupdate_manager()->push_back( DBTREE::url_subject( url ), open );
+            CORE::get_checkupdate_manager()->push_back( DBTREE::url_boardbase( url ), open );
 
     }
 
@@ -1813,7 +1825,7 @@
     if( m_path_selected.empty() ) return;
     std::string url = path2url( m_path_selected );
 
-    SKELETON::PrefDiag* pref= CORE::PrefDiagFactory( get_parent_win(), CORE::PREFDIAG_BOARD, DBTREE::url_subject( url ) );
+    SKELETON::PrefDiag* pref= CORE::PrefDiagFactory( get_parent_win(), CORE::PREFDIAG_BOARD, DBTREE::url_boardbase( url ) );
     pref->run();
     delete pref;
 }
@@ -1889,15 +1901,16 @@
 //
 const bool BBSListViewBase::open_row( Gtk::TreePath& path, const bool tab )
 {
-    if( ! m_treeview.get_row( path ) ) return false;
+    const Gtk::TreeModel::Row row = m_treeview.get_row( path );
+    if( ! row ) return false;
 
     std::string str_tab = "false";
     if( tab ) str_tab = "opentab";
 
     const std::string str_mode = "";
 
-    const Glib::ustring url = path2url( path );
-    const int type = path2type( path );
+    const std::string url = row2url( row );
+    const int type = row2type( row );
 
     if( type != TYPE_DIR && url.empty() ) return false;
 
@@ -1905,14 +1918,14 @@
 
         case TYPE_BOARD:
         case TYPE_BOARD_UPDATE:
-            CORE::core_set_command( "open_board", DBTREE::url_subject( url ), str_tab, str_mode );
+            CORE::core_set_command( "open_board", url, str_tab, str_mode );
             break;
 
         case TYPE_THREAD_OLD:
             toggle_articleicon( url ); // break;しない
         case TYPE_THREAD:
         case TYPE_THREAD_UPDATE:
-            CORE::core_set_command( "open_article", DBTREE::url_dat( url ), str_tab, str_mode );
+            CORE::core_set_command( "open_article", url, str_tab, str_mode );
             break;
 
         case TYPE_IMAGE:
@@ -1979,16 +1992,14 @@
     for( ; it != list_it.end(); ++it ){
 
         Gtk::TreeModel::Row row = *( *it );
-        Gtk::TreePath path = GET_PATH( row );
 
-        int type = path2type( path );
-        std::string url = path2url( path );
+        int type = row2type( row );
+        std::string url = row2url( row );
 
         switch( type ){
 
             case TYPE_BOARD:
             case TYPE_BOARD_UPDATE:
-                url = DBTREE::url_subject( url );
                 if( !list_url_board.empty() ) list_url_board += " ";
                 list_url_board += url;
                 break;
@@ -1996,7 +2007,6 @@
             case TYPE_THREAD:
             case TYPE_THREAD_UPDATE:
             case TYPE_THREAD_OLD:
-                url = DBTREE::url_dat( url );
                 if( !list_url_article.empty() ) list_url_article += " ";
                 list_url_article += url;
                 break;
@@ -2025,14 +2035,13 @@
     for( ; it != list_it.end(); ++it ){
 
         Gtk::TreeModel::Row row = *( *it );
-        Gtk::TreePath path = GET_PATH( row );
 
-        int type = path2type( path );
-        std::string url = path2url( path );
+        int type = row2type( row );
+        std::string url = row2url( row );
 
-        if( type == TYPE_THREAD || type == TYPE_THREAD_UPDATE ) CORE::get_checkupdate_manager()->push_back( DBTREE::url_dat( url ), open );
+        if( type == TYPE_THREAD || type == TYPE_THREAD_UPDATE ) CORE::get_checkupdate_manager()->push_back( url, open );
         else if( CONFIG::get_check_update_board() && ( type == TYPE_BOARD || type == TYPE_BOARD_UPDATE ) )
-            CORE::get_checkupdate_manager()->push_back( DBTREE::url_subject( url ), open );
+            CORE::get_checkupdate_manager()->push_back( url, open );
     }
 }
 
@@ -2060,32 +2069,21 @@
 //
 // path -> url 変換
 //
-const Glib::ustring BBSListViewBase::path2rawurl( const Gtk::TreePath& path )
+std::string BBSListViewBase::path2rawurl( const Gtk::TreePath& path )
 {
-    Gtk::TreeModel::Row row = m_treeview.get_row( path );
-    if( !row ) return Glib::ustring();
-    Glib::ustring url =  row[ m_columns.m_url ];
-    return url;
+    const Gtk::TreeModel::Row row = m_treeview.get_row( path );
+    return row2url( row );
 }
 
 
 // 移転をチェックするバージョン
-const Glib::ustring BBSListViewBase::path2url( const Gtk::TreePath& path )
+std::string BBSListViewBase::path2url( const Gtk::TreePath& path )
 {
-    Gtk::TreeModel::Row row = m_treeview.get_row( path );
-    if( !row ) return Glib::ustring();
+    const Gtk::TreeModel::Row row = m_treeview.get_row( path );
+    std::string url = row2url( row );
 
-    Glib::ustring url =  row[ m_columns.m_url ];
-    if( url.empty() ) return url;
-
-    // 移転があったら url を最新のものに変換しておく
-    int type = path2type( path );
-    switch( type ){
-
-        case TYPE_BOARD:
-        case TYPE_BOARD_UPDATE:
-            url = DBTREE::url_boardbase( url );
-            break;
+    // url を最新のものに変換しておく
+    switch( row2type( row ) ){
 
         case TYPE_THREAD:
         case TYPE_THREAD_UPDATE:
@@ -2104,29 +2102,12 @@
 // 板の場合は boardbase
 // スレの場合は dat 型のアドレスを返す
 //
-const Glib::ustring BBSListViewBase::row2url( const Gtk::TreeModel::Row& row )
+std::string BBSListViewBase::row2url( const Gtk::TreeModel::Row& row )
 {
-    if( ! row ) return Glib::ustring();
-
-    Glib::ustring url =  row[ m_columns.m_url ];
-    if( url.empty() ) return url;
-
-    // 移転があったら url を最新のものに変換しておく
-    int type = row2type( row );
-    switch( type ){
-
-        case TYPE_BOARD:
-        case TYPE_BOARD_UPDATE:
-            url = DBTREE::url_boardbase( url );
-            break;
-
-        case TYPE_THREAD:
-        case TYPE_THREAD_UPDATE:
-        case TYPE_THREAD_OLD:
-            url = DBTREE::url_dat( url );
-            break;
-    }
-
+    std::string url;
+    if( ! row ) return url;
+    const Glib::ustring ustr_url = row[ m_columns.m_url ];
+    url = ustr_url.raw();
     return url;
 }
 
@@ -2135,50 +2116,51 @@
 //
 // path -> name 変換
 //
-const Glib::ustring BBSListViewBase::path2name( const Gtk::TreePath& path )
+std::string BBSListViewBase::path2name( const Gtk::TreePath& path )
 {
-    Gtk::TreeModel::Row row = m_treeview.get_row( path );
-    if( !row ) return Glib::ustring();
-    return row[ m_columns.m_name ];
+    const Gtk::TreeModel::Row row = m_treeview.get_row( path );
+    return row2name( row );
 }
 
 
 
 //
-// path -> type 変換
+// row -> name 変換
 //
-const int BBSListViewBase::path2type( const Gtk::TreePath& path )
+std::string BBSListViewBase::row2name( const Gtk::TreeModel::Row& row )
 {
-    Gtk::TreeModel::Row row = m_treeview.get_row( path );
-    if( !row ) return TYPE_UNKNOWN;
-    return row[ m_columns.m_type ];
+    std::string name;
+    if( !row ) return name;
+    const Glib::ustring ustr_name = row[ m_columns.m_name ];
+    name = ustr_name.raw();
+    return name;
 }
 
 
 //
-// row -> type 変換
+// path -> type 変換
 //
-const int BBSListViewBase::row2type( const Gtk::TreeModel::Row& row )
+int BBSListViewBase::path2type( const Gtk::TreePath& path )
 {
-    if( ! row ) return TYPE_UNKNOWN;
-    return row[ m_columns.m_type ];
+    const Gtk::TreeModel::Row row = m_treeview.get_row( path );
+    return row2type( row );
 }
 
 
 //
-// row -> name 変換
+// row -> type 変換
 //
-const Glib::ustring BBSListViewBase::row2name( const Gtk::TreeModel::Row& row )
+int BBSListViewBase::row2type( const Gtk::TreeModel::Row& row )
 {
-    if( !row ) return Glib::ustring();
-    return row[ m_columns.m_name ];
+    if( ! row ) return TYPE_UNKNOWN;
+    return row[ m_columns.m_type ];
 }
 
 
 //
 // row -> dirid 変換
 //
-const size_t BBSListViewBase::row2dirid( const Gtk::TreeModel::Row& row )
+size_t BBSListViewBase::row2dirid( const Gtk::TreeModel::Row& row )
 {
     if( !row ) return 0;
     return row[ m_columns.m_dirid ];
@@ -2351,7 +2333,7 @@
     for( ; ! it.end(); ++it ){
 
         Gtk::TreeModel::Row row = *it;
-        const Glib::ustring url = row[ m_columns.m_url ];
+        const Glib::ustring ustr_url = row[ m_columns.m_url ];
         const int type = row[ m_columns.m_type ];
 
 #ifdef _DEBUG
@@ -2364,12 +2346,12 @@
             case TYPE_BOARD: // 板
             case TYPE_BOARD_UPDATE:
 
-                url_new = DBTREE::url_boardbase( url );
-                if( url != url_new ){
+                url_new = DBTREE::url_boardbase( ustr_url.raw() );
+                if( ustr_url.raw() != url_new ){
                     updated = true;
                     row[ m_columns.m_url ] = url_new;
 #ifdef _DEBUG
-                    std::cout << url << " -> " << url_new << std::endl;
+                    std::cout << ustr_url << " -> " << url_new << std::endl;
 #endif
                 }
 
@@ -2377,19 +2359,19 @@
                 break;
 
             case TYPE_VBOARD:
-                m_set_board.insert( url );
+                m_set_board.insert( ustr_url.raw() );
                 break;
 
             case TYPE_THREAD: // スレ
             case TYPE_THREAD_UPDATE:
             case TYPE_THREAD_OLD:
 
-                url_new = DBTREE::url_dat( url );
-                if( url != url_new ){
+                url_new = DBTREE::url_dat( ustr_url.raw() );
+                if( ustr_url.raw() != url_new ){
                     updated = true;
                     row[ m_columns.m_url ] = url_new;
 #ifdef _DEBUG
-                    std::cout << url << " -> " << url_new << std::endl;
+                    std::cout << ustr_url << " -> " << url_new << std::endl;
 #endif
                 }
 
@@ -2397,7 +2379,7 @@
                 break;
 
             case TYPE_IMAGE:
-                m_set_image.insert( url );
+                m_set_image.insert( ustr_url.raw() );
                 break;
         }
     }
@@ -2442,7 +2424,7 @@
 
         if( type_row == TYPE_THREAD || type_row == TYPE_THREAD_UPDATE || type_row == TYPE_THREAD_OLD ){
 
-            if( url == url_row ){
+            if( url == url_row.raw() ){
 #ifdef _DEBUG
                 std::cout << "hit " << url << " == " << url_row << std::endl;
                 std::cout << row2name( row ) << std::endl;
@@ -2491,7 +2473,7 @@
 
         if( type_row == TYPE_BOARD || type_row == TYPE_BOARD_UPDATE ){
 
-            if( url_boardbase == url_row ){
+            if( url_boardbase == url_row.raw() ){
 #ifdef _DEBUG
                 std::cout << "hit " << url_boardbase << " == " << url_row << std::endl;
                 std::cout << row2name( row ) << std::endl;
@@ -2517,14 +2499,18 @@
     if( ! m_ready_tree ) return;
     if( m_treestore->children().empty() ) return;
 
-    std::string url_item( url );
+    std::string url_item;
 
-    if( m_set_thread.find_if( url_item )
-            || m_set_image.find( url_item ) != m_set_image.end()){
+    if( m_set_thread.find_if( url ) ||
+        m_set_image.find( url ) != m_set_image.end()){
         // スレまたは画像の場合
+        url_item = url;
     }
     else {
         // 板の場合
+        if( get_url() == URL_HISTTHREADVIEW || get_url() == URL_HISTCLOSEVIEW ||
+            get_url() == URL_HISTCLOSEIMGVIEW ) return;
+
         url_item = DBTREE::url_boardbase( url );
 
         // 未登録の画像などで、板が見つからない場合は処理しない
@@ -2545,9 +2531,10 @@
     for( ; ! it.end(); ++it ){
 
         Gtk::TreeModel::Row row = *it;
-        Gtk::TreePath path = GET_PATH( row );
+        const Glib::ustring ustr_url = row[ m_columns.m_url ];
 
-        if( url_item == row[ m_columns.m_url ] || url_item == path2url( path ) ){
+        if( url_item == ustr_url.raw() ){
+            path = GET_PATH( row );
 
             // 最初に見つかったものにフォーカスする
             if( m_treeview.is_expand( path ) ){
@@ -2593,7 +2580,7 @@
     const std::string urldat_new = DBTREE::url_dat( url_new );
     if( urldat_new.empty() ) return;
 
-    const std::string name_new = DBTREE::article_subject( urldat_new );
+    const std::string name_new = MISC::to_plain( DBTREE::article_modified_subject( urldat_new ) );
     if( name_new.empty() ) return;
 
     bool show_diag = CONFIG::show_diag_replace_favorite();
@@ -2601,8 +2588,7 @@
     if( ! show_diag && mode == REPLACE_NEXT_NO ) return;
 
     const std::string urldat = DBTREE::url_dat( url );
-    const std::string urlcgi = DBTREE::url_readcgi( url, 0, 0 );
-    const std::string name_old = MISC::remove_space( DBTREE::article_subject( urldat ) );
+    const std::string name_old = MISC::to_plain( DBTREE::article_modified_subject( urldat ) );
 
     int type = TYPE_THREAD;
     const int status = DBTREE::article_status( urldat_new );
@@ -2619,7 +2605,7 @@
     for( ; ! it.end(); ++it ){
 
         Gtk::TreeModel::Row row = *it;
-        const Glib::ustring url_row = row[ m_columns.m_url ];
+        const Glib::ustring ustr_url = row[ m_columns.m_url ];
 
         switch( row[ m_columns.m_type ] ){
 
@@ -2627,7 +2613,7 @@
             case TYPE_THREAD_UPDATE:
             case TYPE_THREAD_OLD:
 
-                if( urldat == url_row || urlcgi == url_row ){
+                if( urldat == ustr_url.raw() ){
 
                     if( show_diag ){
 
@@ -2672,11 +2658,11 @@
 
                         // 名前が古いものであったら更新
                         // 手動で変更されていたらそのまま
-                        const Glib::ustring name_row = row[ m_columns.m_name ];
+                        const Glib::ustring ustr_name = row[ m_columns.m_name ];
 #ifdef _DEBUG
-                        std::cout << "name_row = " << name_row << std::endl;
+                        std::cout << "name_row = " << ustr_name << std::endl;
 #endif
-                        if( MISC::remove_space( name_row ) == name_old ){
+                        if( ustr_name.raw() == name_old ){
 #ifdef _DEBUG
                             std::cout << "replace name\n";
 #endif
@@ -2718,9 +2704,6 @@
 //
 void BBSListViewBase::exec_search()
 {
-    JDLIB::Regex regex_name;
-    JDLIB::Regex regex_url;
-
     CORE::core_set_command( "set_info", "", "" );
 
     std::string query = get_search_query();
@@ -2753,13 +2736,15 @@
     const bool newline_name = true; // . に改行をマッチさせない
     const bool usemigemo_name = true; // migemo使用
     const bool wchar_name = true;  // 全角半角の区別をしない
-    regex_name.compile( query, icase_name, newline_name, usemigemo_name, wchar_name );
+    JDLIB::RegexPattern regex_name( query, icase_name, newline_name, usemigemo_name, wchar_name );
 
     const bool icase_url = true; // 大文字小文字区別しない
     const bool newline_url = true;
     const bool usemigemo_url = false;
     const bool wchar_url = false;
-    regex_url.compile( query, icase_url, newline_url, usemigemo_url, wchar_url );
+    JDLIB::RegexPattern regex_url( query, icase_url, newline_url, usemigemo_url, wchar_url );
+
+    JDLIB::Regex regex;
 
     bool hit = false;
     for(;;){
@@ -2790,11 +2775,11 @@
             else path = m_treeview.prev_path( path, false );
         }
 
-        Glib::ustring name = path2name( path );
-        Glib::ustring url = path2url( path );
+        const std::string name = path2name( path );
+        const std::string url = path2url( path );
 
         const size_t offset = 0;
-        if( regex_name.exec( name, offset ) || regex_url.exec( url, offset ) ) hit = true;
+        if( regex.match( regex_name, name, offset ) || regex.match( regex_url, url, offset ) ) hit = true;
 
         // 一周したら終わり
         if( path == path_start ) break;
@@ -3057,7 +3042,7 @@
     for( ; ! it.end(); ++it ){
 
         Gtk::TreeModel::Row row = *it;
-        const Glib::ustring url = row[ m_columns.m_url ];
+        const Glib::ustring ustr_url = row[ m_columns.m_url ];
         const int type = row[ m_columns.m_type ];
 
         switch( type ){
@@ -3071,7 +3056,7 @@
 
             case TYPE_IMAGE: // 画像
 
-                if( url == url_target ){
+                if( ustr_url.raw() == url_target ){
 #ifdef _DEBUG
                     std::cout << "hit " << url << " == " << url_target << std::endl;
                     std::cout << row2name( row ) << std::endl;
diff -aurNZ jd-2.8.9-150226/src/bbslist/bbslistviewbase.h jd-2.8.9-a171004/src/bbslist/bbslistviewbase.h
--- jd-2.8.9-150226/src/bbslist/bbslistviewbase.h	2013-07-28 15:49:29.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/bbslistviewbase.h	2016-03-15 17:57:10.570920000 +0900
@@ -135,28 +135,28 @@
         void replace_thread( const std::string& url, const std::string& url_new );
 
         // path からその行のタイプを取得
-        const int path2type( const Gtk::TreePath& path );
+        int path2type( const Gtk::TreePath& path );
 
         // row からタイプを取得
-        const int row2type( const Gtk::TreeModel::Row& row );
+        int row2type( const Gtk::TreeModel::Row& row );
 
         // row -> name 変換
-        const Glib::ustring row2name( const Gtk::TreeModel::Row& row );
+        std::string row2name( const Gtk::TreeModel::Row& row );
 
         // row -> url 変換
         // 板の場合は boardbase
         // スレの場合は dat 型のアドレスを返す
-        const Glib::ustring row2url( const Gtk::TreeModel::Row& row );
+        std::string row2url( const Gtk::TreeModel::Row& row );
 
         // row -> dirid 変換
-        const size_t row2dirid( const Gtk::TreeModel::Row& row );
+        size_t row2dirid( const Gtk::TreeModel::Row& row );
 
         // path からその行の名前を取得
-        const Glib::ustring path2name( const Gtk::TreePath& path );
+        std::string path2name( const Gtk::TreePath& path );
 
         // path からその行のURLを取得
-        const Glib::ustring path2rawurl( const Gtk::TreePath& path );
-        const Glib::ustring path2url( const Gtk::TreePath& path ); // 移転をチェックするバージョン
+        std::string path2rawurl( const Gtk::TreePath& path );
+        std::string path2url( const Gtk::TreePath& path ); // 移転をチェックするバージョン
 
         // url で指定した項目を削除
         void remove_item( const std::string& url );
@@ -193,7 +193,7 @@
         // 親ウィンドウをセット
         virtual void set_parent_win( Gtk::Window* parent_win );
 
-        virtual const std::string url_for_copy(){ return std::string(); }
+        virtual const std::string url_for_copy() const { return std::string(); }
 
         virtual const bool set_command( const std::string& command,
                                         const std::string& arg1 = std::string(),
@@ -207,7 +207,7 @@
 
         virtual void stop();
         virtual void redraw_view();
-        virtual void relayout();  // 色やフォントなどの変更
+        virtual void relayout( const bool completely = false );  // 色やフォントなどの変更
         virtual void focus_view();
         virtual void focus_out();
         virtual void close_view();
diff -aurNZ jd-2.8.9-150226/src/bbslist/editlistwin.cpp jd-2.8.9-a171004/src/bbslist/editlistwin.cpp
--- jd-2.8.9-150226/src/bbslist/editlistwin.cpp	2009-05-03 12:18:09.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/editlistwin.cpp	2017-04-05 22:28:25.432380000 +0900
@@ -74,6 +74,10 @@
 }
 
 
+EditListWin::~EditListWin()
+{}
+
+
 void EditListWin::clock_in()
 {
     if( m_selectview ) m_selectview->clock_in();
diff -aurNZ jd-2.8.9-150226/src/bbslist/editlistwin.h jd-2.8.9-a171004/src/bbslist/editlistwin.h
--- jd-2.8.9-150226/src/bbslist/editlistwin.h	2008-12-28 23:18:16.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/editlistwin.h	2017-04-05 21:33:08.296431000 +0900
@@ -26,6 +26,7 @@
       public:
 
         EditListWin( const std::string& url, Glib::RefPtr< Gtk::TreeStore >& treestore );
+        virtual ~EditListWin();
 
         void clock_in();
         void append_item();
diff -aurNZ jd-2.8.9-150226/src/bbslist/favoriteview.cpp jd-2.8.9-a171004/src/bbslist/favoriteview.cpp
--- jd-2.8.9-150226/src/bbslist/favoriteview.cpp	2010-11-10 23:11:08.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/favoriteview.cpp	2015-07-01 19:27:03.395457000 +0900
@@ -56,12 +56,7 @@
 {
     std::string xml;
 
-    // ファイルが存在しなければ入力を旧ファイル名にする
     std::string file_in = CACHE::path_xml_favorite();
-    if( CACHE::file_exists( file_in ) != CACHE::EXIST_FILE )
-    {
-    	file_in = CACHE::path_xml_favorite_old();
-    }
 
     CACHE::load_rawdata( file_in, xml );
 
diff -aurNZ jd-2.8.9-150226/src/bbslist/selectlistview.cpp jd-2.8.9-a171004/src/bbslist/selectlistview.cpp
--- jd-2.8.9-150226/src/bbslist/selectlistview.cpp	2008-12-28 23:18:16.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/selectlistview.cpp	2017-04-05 21:32:49.952432000 +0900
@@ -20,6 +20,10 @@
 }
 
 
+SelectListView::~SelectListView()
+{}
+
+
 void SelectListView::close_view()
 {
 #ifdef _DEBUG
diff -aurNZ jd-2.8.9-150226/src/bbslist/selectlistview.h jd-2.8.9-a171004/src/bbslist/selectlistview.h
--- jd-2.8.9-150226/src/bbslist/selectlistview.h	2010-11-10 23:11:08.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/selectlistview.h	2017-04-05 21:32:24.616432000 +0900
@@ -23,7 +23,7 @@
       public:
 
         SelectListView( const std::string& url, const std::string& arg1 = std::string() , const std::string& arg2 = std::string() );
-        virtual ~SelectListView(){}
+        virtual ~SelectListView();
 
         SIG_CLOSE_DIALOG sig_close_dialog() { return m_sig_close_dialog; }
         SIG_FOCUS_ENTRY_SEARCH sig_focus_entry_search() { return m_sig_focus_entry_search; }
diff -aurNZ jd-2.8.9-150226/src/bbslist/toolbar.cpp jd-2.8.9-a171004/src/bbslist/toolbar.cpp
--- jd-2.8.9-150226/src/bbslist/toolbar.cpp	2012-08-03 23:40:01.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/toolbar.cpp	2017-04-05 21:34:35.984430000 +0900
@@ -62,6 +62,10 @@
 }
 
 
+BBSListToolBar::~BBSListToolBar()
+{}
+
+
 //
 // ボタンのパッキング
 //
diff -aurNZ jd-2.8.9-150226/src/bbslist/toolbar.h jd-2.8.9-a171004/src/bbslist/toolbar.h
--- jd-2.8.9-150226/src/bbslist/toolbar.h	2010-12-28 23:18:42.000000000 +0900
+++ jd-2.8.9-a171004/src/bbslist/toolbar.h	2017-04-05 21:34:02.460431000 +0900
@@ -28,7 +28,7 @@
       public:
 
         BBSListToolBar();
-        virtual ~BBSListToolBar(){}
+        virtual ~BBSListToolBar();
 
         // タブが切り替わった時にDragableNoteBookから呼び出される( Viewの情報を取得する )
         virtual void set_view( SKELETON::View * view );
diff -aurNZ jd-2.8.9-150226/src/board/boardadmin.cpp jd-2.8.9-a171004/src/board/boardadmin.cpp
--- jd-2.8.9-150226/src/board/boardadmin.cpp	2013-07-28 15:49:29.000000000 +0900
+++ jd-2.8.9-a171004/src/board/boardadmin.cpp	2016-01-16 23:30:24.115365000 +0900
@@ -123,8 +123,7 @@
         COMMAND_ARGS command_arg = url_to_openarg( *it_url, true, lock );
 
         // 板がDBに登録されていない場合は表示しない
-        if( command_arg.url != URL_ALLLOG && command_arg.arg4 != "SIDEBAR"
-            && DBTREE::url_boardbase( command_arg.url ).empty() ){
+        if( command_arg.url.empty() && command_arg.arg4 != "SIDEBAR" ){
             MISC::ERRMSG(  *it_url + " is not registered" );
             list_switchhistory.remove( *it_url );
             continue;
@@ -163,7 +162,7 @@
     // 次スレ検索
     if( regex.exec( std::string( "(.*)" ) + NEXT_SIGN + ARTICLE_SIGN + "(.*)", url, offset, icase, newline, usemigemo, wchar )){
 
-        command_arg.url = regex.str( 1 );
+        command_arg.url = DBTREE::url_boardbase( regex.str( 1 ) );
 
         command_arg.arg4 = "NEXT";
         command_arg.arg5 = regex.str( 2 ); // 前スレのアドレス
@@ -180,7 +179,7 @@
     // ログ一覧
     else if( regex.exec( std::string( "(.*)" ) + LOG_SIGN, url, offset, icase, newline, usemigemo, wchar )){
 
-        command_arg.url = regex.str( 1 );
+        command_arg.url = DBTREE::url_boardbase( regex.str( 1 ) );
 
         command_arg.arg4 = "LOG";
     }
@@ -188,7 +187,7 @@
     // サイドバー
     else if( regex.exec( std::string( "(.*)" ) + SIDEBAR_SIGN + "(.*)", url, offset, icase, newline, usemigemo, wchar )){
 
-        command_arg.url = regex.str( 1 );
+        command_arg.url = DBTREE::url_boardbase( regex.str( 1 ) );
 
         command_arg.arg4 = "SIDEBAR";
         command_arg.arg5 = regex.str( 2 ); // ディレクトリID
@@ -197,7 +196,7 @@
     // スレビュー
     else{
 
-        command_arg.url = url;
+        command_arg.url = DBTREE::url_boardbase( url );
 
         command_arg.arg4 = "MAIN";
     }
diff -aurNZ jd-2.8.9-150226/src/board/boardview.cpp jd-2.8.9-a171004/src/board/boardview.cpp
--- jd-2.8.9-150226/src/board/boardview.cpp	2011-06-21 00:21:47.000000000 +0900
+++ jd-2.8.9-a171004/src/board/boardview.cpp	2016-03-15 21:07:25.638745000 +0900
@@ -62,7 +62,7 @@
 
 
 // 更新した
-const bool BoardView::is_updated()
+const bool BoardView::is_updated() const
 {
     const int status = DBTREE::board_status( get_url_board() );
 
@@ -75,7 +75,7 @@
 
 
 // 更新チェックして更新可能か
-const bool BoardView::is_check_update()
+const bool BoardView::is_check_update() const
 {
     const int status = DBTREE::board_status( get_url_board() );
 
diff -aurNZ jd-2.8.9-150226/src/board/boardview.h jd-2.8.9-a171004/src/board/boardview.h
--- jd-2.8.9-150226/src/board/boardview.h	2010-09-02 00:33:56.000000000 +0900
+++ jd-2.8.9-a171004/src/board/boardview.h	2016-03-15 21:07:28.186745000 +0900
@@ -20,8 +20,8 @@
 
         virtual void save_session();
 
-        virtual const bool is_updated();
-        virtual const bool is_check_update();
+        virtual const bool is_updated() const;
+        virtual const bool is_check_update() const;
 
         virtual void reload();
         virtual void show_view();
diff -aurNZ jd-2.8.9-150226/src/board/boardviewbase.cpp jd-2.8.9-a171004/src/board/boardviewbase.cpp
--- jd-2.8.9-150226/src/board/boardviewbase.cpp	2014-04-13 03:12:55.000000000 +0900
+++ jd-2.8.9-a171004/src/board/boardviewbase.cpp	2016-05-20 16:36:10.246638000 +0900
@@ -125,8 +125,8 @@
 {
     // 次スレ検索ビューのようにURLの途中に http が入っている場合は取り除く
     const size_t pos = url.rfind( "http://" );
-    if( pos != std::string::npos && pos != 0 ) m_url_board = DBTREE::url_subject( url.substr( 0, pos ) );
-    else m_url_board = DBTREE::url_subject( url );
+    if( pos != std::string::npos && pos != 0 ) m_url_board = DBTREE::url_boardbase( url.substr( 0, pos ) );
+    else m_url_board = DBTREE::url_boardbase( url );
 
     m_scrwin.add( m_treeview );
     m_scrwin.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS );
@@ -219,7 +219,7 @@
 
 
 // アイコンのID取得
-const int BoardViewBase::get_icon( const std::string& iconname )
+const int BoardViewBase::get_icon( const std::string& iconname ) const
 {
     int id = ICON::NONE;
 
@@ -240,9 +240,9 @@
 //
 // コピー用URL(メインウィンドウのURLバーなどに表示する)
 //
-const std::string BoardViewBase::url_for_copy()
+const std::string BoardViewBase::url_for_copy() const
 {
-    return DBTREE::url_boardbase( get_url_board() );
+    return get_url_board();
 }
 
 
@@ -675,7 +675,10 @@
         Gtk::CellRenderer *cell = column->get_first_cell_renderer();
 
         // 実際の描画の際に cellrendere のプロパティをセットするスロット関数
-        if( cell ) column->set_cell_data_func( *cell, sigc::mem_fun( *this, &BoardViewBase::slot_cell_data ) );
+        if( cell ){
+            if( id == COL_SUBJECT ) column->set_cell_data_func( *cell, sigc::mem_fun( *this, &BoardViewBase::slot_cell_data_markup ) );
+            else column->set_cell_data_func( *cell, sigc::mem_fun( *this, &BoardViewBase::slot_cell_data ) );
+        }
 
         Gtk::CellRendererText* rentext = dynamic_cast< Gtk::CellRendererText* >( cell );
         if( rentext ){
@@ -828,6 +831,33 @@
 
 
 //
+// Subjectの実際の描画の際に cellrendere のプロパティをセットするスロット関数
+//
+void BoardViewBase::slot_cell_data_markup( Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& it )
+{
+    Gtk::TreeModel::Row row = *it;
+    Gtk::TreePath path = GET_PATH( row );
+
+#ifdef _DEBUG
+//    std::cout << "BoardViewBase::slot_cell_data path = " << path.to_string() << std::endl;
+#endif
+
+    // ハイライト色 ( 抽出状態 )
+    if( row[ m_columns.m_col_drawbg ] ){
+        cell->property_cell_background() = CONFIG::get_color( COLOR_BACK_HIGHLIGHT_TREE );
+        cell->property_cell_background_set() = true;
+    }
+
+    else m_treeview.slot_cell_data( cell, it );
+
+    Gtk::CellRendererText* rentext = dynamic_cast< Gtk::CellRendererText* >( cell );
+    rentext->property_text() = "";
+    rentext->property_markup() = row[ m_columns.m_col_subject ];
+}
+
+
+
+//
 // 実際の描画の際に cellrendere のプロパティをセットするスロット関数
 //
 void BoardViewBase::slot_cell_data( Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& it )
@@ -1258,7 +1288,7 @@
 //
 // 色、フォント、表示内容の更新
 //
-void BoardViewBase::relayout()
+void BoardViewBase::relayout( const bool completely )
 {
     m_treeview.init_color( COLOR_CHAR_BOARD, COLOR_BACK_BOARD, COLOR_BACK_BOARD_EVEN );
     m_treeview.init_font( CONFIG::get_fontname( FONT_BOARD ) );
@@ -1341,6 +1371,8 @@
 //
 void BoardViewBase::select_item( const std::string& url )
 {
+    if( m_url_board != DBTREE::url_boardbase( url ) ) return;
+
     const Gtk::TreeModel::Row row = get_row_from_url( url );
     if( row ){
         Gtk::TreePath path = GET_PATH( row );
@@ -1757,14 +1789,14 @@
 //
 // 特定の行だけの表示内容更新
 //
-// url : subject.txt のアドレス
+// url : boardbase アドレス
 // id : DAT の ID(拡張子付き), empty なら全ての行の表示内容を更新する
 //
 void BoardViewBase::update_item( const std::string& url, const std::string& id )
 {
     if( is_loading() ) return;
     if( ! get_row_size() ) return;
-    if( ! get_url_board().empty() && get_url_board() !=  url ) return;
+    if( get_url_board() !=  url ) return;
 
     if( id.empty() ){
         update_item_all();
@@ -1850,7 +1882,7 @@
     const int res = art->get_number();
 
     // タイトル、レス数、抽出
-    row[ m_columns.m_col_subject ] = art->get_subject();
+    row[ m_columns.m_col_subject ] = MISC::to_markup( art->get_modified_subject( true ) );
     row[ m_columns.m_col_res ] = res;
 
     // 読み込み数
@@ -1874,6 +1906,10 @@
         row[ m_columns.m_col_new ] = -1;
     }
 
+    // 速度
+    if( ( art->get_status() & STATUS_NORMAL ) && ! art->is_924() )
+        row[ m_columns.m_col_speed ] = art->get_speed();
+
 
     //
     // マーク
@@ -2139,7 +2175,7 @@
         m_treeview.set_tooltip_min_width( column->get_width() );
 
         if( column->get_title() == ITEM_NAME_BOARD ) m_treeview.set_str_tooltip( get_name_of_cell( path, m_columns.m_col_board ) );
-        else if( column->get_title() == ITEM_NAME_NAME ) m_treeview.set_str_tooltip( get_name_of_cell( path, m_columns.m_col_subject ) );
+        else if( column->get_title() == ITEM_NAME_NAME ) m_treeview.set_str_tooltip( get_name_of_cell( path, m_columns.m_col_subject ), true );
         else if( column->get_title() == ITEM_NAME_SINCE ) m_treeview.set_str_tooltip( get_name_of_cell( path, m_columns.m_col_since ) );
         else if( column->get_title() == ITEM_NAME_LASTWRITE ) m_treeview.set_str_tooltip( get_name_of_cell( path, m_columns.m_col_write ) );
         else if( column->get_title() == ITEM_NAME_ACCESS ) m_treeview.set_str_tooltip( get_name_of_cell( path, m_columns.m_col_access ) );
@@ -2381,7 +2417,7 @@
     if( m_path_selected.empty() ) return;
 
     const std::string url = DBTREE::url_readcgi( path2daturl( m_path_selected ), 0, 0 );
-    const std::string name = DBTREE::article_subject( url );
+    const std::string name = MISC::to_plain( DBTREE::article_subject( url ) );
 
     MISC::CopyClipboard( name + '\n' + url );
 }
@@ -2546,7 +2582,7 @@
 {
     if( ! get_url_board().empty() ) return get_url_board();
     if( path.empty() ) return std::string();
-    return DBTREE::url_subject( path2daturl( path ) );
+    return DBTREE::url_boardbase( path2daturl( path ) );
 }
 
 
@@ -2570,15 +2606,17 @@
     unsorted_column();
 
     JDLIB::Regex regex;
+    JDLIB::RegexPattern regexptn;
     const bool icase = true; // 大文字小文字区別しない
     const bool newline = true; // . に改行をマッチさせない
     const bool usemigemo = true; // migemo使用
-    const bool wchar = true;  // 全角半角の区別をしない
+    const bool wchar = false;  // 全角半角の区別をしない
+    const bool norm = true; // Unicodeの互換文字を区別しない
 
     Gtk::TreeModel::Children child = m_liststore->children();
     Gtk::TreeModel::Children::iterator it = child.begin();
 
-    if ( ! reset ) regex.compile( query, icase, newline, usemigemo, wchar );
+    if ( ! reset ) regexptn.set( query, icase, newline, usemigemo, wchar, norm );
 
     for( ; it != child.end() ; ++it ){
 
@@ -2586,7 +2624,7 @@
         const Glib::ustring subject = row[ m_columns.m_col_subject ];
 
         if( reset ) row[ m_columns.m_col_drawbg ] = false;
-        else if( regex.exec( subject, 0 ) ){
+        else if( regex.match( regexptn, MISC::to_plain( subject ), 0 ) ){
             row[ m_columns.m_col_drawbg ] = true;
             ++hit;
 
@@ -2647,7 +2685,10 @@
     Gtk::TreePath path = m_treeview.get_current_path();;
     if( path.empty() ){
         if( m_search_invert ) path = GET_PATH( *( m_liststore->children().begin() ) );
-        else GET_PATH( *( m_liststore->children().rbegin() ) );
+        else{
+            Gtk::TreeModel::iterator it = m_liststore->children().end();
+            path = GET_PATH( *--it );
+        }
     }
 
     Gtk::TreePath path_start = path;
@@ -2656,7 +2697,8 @@
     const bool icase = true; // 大文字小文字区別しない
     const bool newline = true; // . に改行をマッチさせない
     const bool usemigemo = true; // migemo使用
-    const bool wchar = true;  // 全角半角の区別をしない
+    const bool wchar = false;  // 全角半角の区別をしない
+    const bool norm = true; // Unicodeの互換文字を区別しない
 
 #ifdef _DEBUG
     std::cout << "BoardViewBase::search start = " << path_start.to_string() << " query = " <<  query << std::endl;
@@ -2674,7 +2716,8 @@
             // 前へ
             if( ! path.prev() ){
                 // 一番後へ
-                path =  GET_PATH( *( m_liststore->children().rbegin() ) );
+                Gtk::TreeModel::iterator it = m_liststore->children().end();
+                path = GET_PATH( *--it );
             }
         }
 
@@ -2682,7 +2725,7 @@
         if( path == path_start ) break;
 
         Glib::ustring subject = get_name_of_cell( path, m_columns.m_col_subject );
-        if( regex.exec( query, subject, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.exec( query, MISC::to_plain( subject ), offset, icase, newline, usemigemo, wchar, norm ) ){
             m_treeview.scroll_to_row( path, 0 );
             m_treeview.set_cursor( path );
             return;
@@ -2922,7 +2965,7 @@
     if( m_path_selected.empty() ) return;
     const std::string url = path2daturl( m_path_selected );
 
-    CORE::core_set_command( "open_board_next", DBTREE::url_subject( url ) , url );
+    CORE::core_set_command( "open_board_next", DBTREE::url_boardbase( url ) , url );
 }
 
 
@@ -2946,10 +2989,11 @@
     // あぼーん情報更新
     std::list< std::string > words = DBTREE::get_abone_list_word_thread( get_url_board() );
     std::list< std::string > regexs = DBTREE::get_abone_list_regex_thread( get_url_board() );
-    const int number = DBTREE::get_abone_number_thread( get_url_board() );
+    const int min_number = DBTREE::get_abone_min_number_thread( get_url_board() );
+    const int max_number = DBTREE::get_abone_max_number_thread( get_url_board() );
     const int hour = DBTREE::get_abone_hour_thread( get_url_board() );
     const bool redraw = false; // 板の再描画はしない
-    DBTREE::reset_abone_thread( get_url_board(), threads, words, regexs, number, hour, redraw );
+    DBTREE::reset_abone_thread( get_url_board(), threads, words, regexs, min_number, max_number, hour, redraw );
 
     m_treeview.delete_selected_rows( true );
 }
@@ -2992,7 +3036,7 @@
             info.type = TYPE_THREAD;
             info.parent = BOARD::get_admin()->get_win();
             info.url = art->get_url();
-            info.name = name.raw();
+            info.name = MISC::to_plain( name.raw() );
             info.path = path.to_string();
 
             list_info.push_back( info );
diff -aurNZ jd-2.8.9-150226/src/board/boardviewbase.h jd-2.8.9-a171004/src/board/boardviewbase.h
--- jd-2.8.9-150226/src/board/boardviewbase.h	2013-07-28 15:49:29.000000000 +0900
+++ jd-2.8.9-a171004/src/board/boardviewbase.h	2016-03-15 20:57:57.138754000 +0900
@@ -104,7 +104,7 @@
         virtual ~BoardViewBase();
 
         const std::string& get_url_board() const { return m_url_board; }
-        virtual const std::string url_for_copy();
+        virtual const std::string url_for_copy() const;
 
         // 行数
         const int get_row_size();
@@ -115,8 +115,8 @@
 
         virtual void update_url( const std::string& url_old, const std::string& url_new );
 
-        virtual const int get_icon( const std::string& iconname );
-        virtual const bool is_loading(){ return m_loading; }
+        virtual const int get_icon( const std::string& iconname ) const;
+        virtual const bool is_loading() const { return m_loading; }
         virtual const bool set_command( const std::string& command,
                                         const std::string& arg1 = std::string(),
                                         const std::string& arg2 = std::string()
@@ -131,7 +131,7 @@
         virtual void stop();
         virtual void show_view();
         virtual void redraw_scrollbar();
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
         virtual void focus_view();
         virtual void focus_out();
         virtual void close_view();
@@ -232,6 +232,7 @@
         // 列の幅の保存
         virtual void save_column_width();
 
+        void slot_cell_data_markup( Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& it );
         void slot_cell_data( Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& it );
 
         // 全ての行の表示内容更新
diff -aurNZ jd-2.8.9-150226/src/board/boardviewnext.cpp jd-2.8.9-a171004/src/board/boardviewnext.cpp
--- jd-2.8.9-150226/src/board/boardviewnext.cpp	2010-03-19 00:04:10.000000000 +0900
+++ jd-2.8.9-a171004/src/board/boardviewnext.cpp	2016-02-08 12:30:52.725046000 +0900
@@ -12,6 +12,7 @@
 #include "skeleton/msgdiag.h"
 
 #include "jdlib/tfidf.h"
+#include "jdlib/miscutil.h"
 
 #include "config/globalconf.h"
 
@@ -180,7 +181,7 @@
 //
 void BoardViewNext::update_boardname()
 {
-    const std::string title = "[ 次スレ検索 ] - " + DBTREE::article_subject( m_url_pre_article );
+    const std::string title = "[ 次スレ検索 ] - " + MISC::to_plain( DBTREE::article_modified_subject( m_url_pre_article ) );
 
     // ウィンドウタイトル表示
     set_title( title );
diff -aurNZ jd-2.8.9-150226/src/board/preference.cpp jd-2.8.9-a171004/src/board/preference.cpp
--- jd-2.8.9-150226/src/board/preference.cpp	2011-02-20 00:05:44.000000000 +0900
+++ jd-2.8.9-a171004/src/board/preference.cpp	2017-03-22 18:23:47.744045000 +0900
@@ -10,8 +10,9 @@
 
 #include "skeleton/msgdiag.h"
 
-#include "jdlib/miscutil.h"
+#include "jdlib/misccharcode.h"
 #include "jdlib/misctime.h"
+#include "jdlib/miscutil.h"
 
 #include "config/globalconf.h"
 
@@ -110,10 +111,10 @@
         str_cookies = "クッキー:\n";
         std::list< std::string >::iterator it = list_cookies.begin();
         for( ; it != list_cookies.end(); ++it )
-            str_cookies += MISC::Iconv( (*it), DBTREE::board_charset( get_url() ), "UTF-8" ) + "\n";
+            str_cookies += MISC::Iconv( (*it), DBTREE::board_charcode( get_url() ), CHARCODE_UTF8 ) + "\n";
     }
 
-    const std::string keyword = DBTREE::board_keyword_for_write( get_url() );
+    const std::string& keyword = DBTREE::board_keyword_for_write( get_url() );
     if( ! keyword.empty() ) str_cookies += "\nキーワード: " + keyword + "\n";
 
     m_edit_cookies.set_text( str_cookies );
@@ -147,10 +148,31 @@
     m_hbox_live.set_spacing( 4 );
     m_hbox_live.pack_start( m_label_live, Gtk::PACK_SHRINK );
     m_hbox_live.pack_start( m_spin_live, Gtk::PACK_SHRINK );
-    m_hbox_live.pack_start( m_check_live, Gtk::PACK_SHRINK );
+    m_hbox_live.pack_start( m_check_live );
 
     set_activate_entry( m_spin_live );
 
+    // 文字エンコーディング
+    m_label_charset.set_text( "エンコーディング:" );
+#if GTKMM_CHECK_VERSION(2,24,0)
+    m_combo_charset.append( MISC::charcode_to_cstr( CHARCODE_UTF8 ) );
+    m_combo_charset.append( MISC::charcode_to_cstr( CHARCODE_SJIS ) );
+    m_combo_charset.append( MISC::charcode_to_cstr( CHARCODE_EUCJP ) );
+    m_combo_charset.set_active_text( MISC::charcode_to_cstr( DBTREE::board_charcode( get_url() ) ) );
+#else
+    std::list< Glib::ustring > list_charset;
+    list_charset.push_back( MISC::charcode_to_cstr( CHARCODE_UTF8 ) );
+    list_charset.push_back( MISC::charcode_to_cstr( CHARCODE_SJIS ) );
+    list_charset.push_back( MISC::charcode_to_cstr( CHARCODE_EUCJP ) );
+    m_combo_charset.set_popdown_strings( list_charset );
+    m_combo_charset.get_entry()->set_text( MISC::charcode_to_cstr( DBTREE::board_charcode( get_url() ) ) );
+    m_combo_charset.get_entry()->set_editable( false );
+    m_combo_charset.set_value_in_list( false );
+#endif
+
+    m_hbox_live.pack_start( m_label_charset, Gtk::PACK_SHRINK );
+    m_hbox_live.pack_start( m_combo_charset, Gtk::PACK_SHRINK );
+
     // 一般ページのパッキング
     m_label_max_line.set_text( MISC::itostr( DBTREE::line_number( get_url() ) * 2 ) );
     m_label_max_byte.set_text( MISC::itostr( DBTREE::message_count( get_url() ) ) );
@@ -286,16 +308,27 @@
     // スレ数、時間
     m_label_abone_thread.set_text( "以下の数字が0の時は、設定メニューの全体あぼ〜ん設定で指定した数字が用いられます。\nまたキャッシュにログがあるスレはあぼ〜んされません。\n\n" );
 
-    m_label_number.set_text( "レス以上のスレをあぼ〜ん" );
-    m_spin_number.set_range( 0, 9999 );
-    m_spin_number.set_increments( 1, 1 );
-    m_spin_number.set_value( DBTREE::get_abone_number_thread( get_url() ) );
-            
-    m_hbox_number.set_spacing( 4 );
-    m_hbox_number.pack_start( m_spin_number, Gtk::PACK_SHRINK );
-    m_hbox_number.pack_start( m_label_number, Gtk::PACK_SHRINK );
+    m_label_min_number.set_text( "レス以下のスレをあぼ〜ん" );
+    m_spin_min_number.set_range( 0, MAX_RESNUMBER );
+    m_spin_min_number.set_increments( 1, 1 );
+    m_spin_min_number.set_value( DBTREE::get_abone_min_number_thread( get_url() ) );
+
+    m_hbox_min_number.set_spacing( 4 );
+    m_hbox_min_number.pack_start( m_spin_min_number, Gtk::PACK_SHRINK );
+    m_hbox_min_number.pack_start( m_label_min_number, Gtk::PACK_SHRINK );
+
+    set_activate_entry( m_spin_max_number );
+
+    m_label_max_number.set_text( "レス以上のスレをあぼ〜ん" );
+    m_spin_max_number.set_range( 0, MAX_RESNUMBER );
+    m_spin_max_number.set_increments( 1, 1 );
+    m_spin_max_number.set_value( DBTREE::get_abone_max_number_thread( get_url() ) );
+
+    m_hbox_max_number.set_spacing( 4 );
+    m_hbox_max_number.pack_start( m_spin_max_number, Gtk::PACK_SHRINK );
+    m_hbox_max_number.pack_start( m_label_max_number, Gtk::PACK_SHRINK );
 
-    set_activate_entry( m_spin_number );
+    set_activate_entry( m_spin_max_number );
 
     m_label_hour.set_text( "時間以上スレ立てから経過したスレをあぼ〜ん" );
     m_spin_hour.set_range( 0, 9999 );
@@ -311,7 +344,8 @@
     m_vbox_abone_thread.set_border_width( 16 );
     m_vbox_abone_thread.set_spacing( 8 );
     m_vbox_abone_thread.pack_start( m_label_abone_thread, Gtk::PACK_SHRINK );
-    m_vbox_abone_thread.pack_start( m_hbox_number, Gtk::PACK_SHRINK );
+    m_vbox_abone_thread.pack_start( m_hbox_min_number, Gtk::PACK_SHRINK );
+    m_vbox_abone_thread.pack_start( m_hbox_max_number, Gtk::PACK_SHRINK );
     m_vbox_abone_thread.pack_start( m_hbox_hour, Gtk::PACK_SHRINK );
 
     // スレあぼーん
@@ -400,7 +434,7 @@
     DBTREE::board_clear_all_post_history( get_url() );
 
     // スレ一覧とスレビューの表示更新
-    CORE::core_set_command( "update_board", DBTREE::url_subject( get_url() ) );
+    CORE::core_set_command( "update_board", DBTREE::url_boardbase( get_url() ) );
     CORE::core_set_command( "redraw_article" );
 }
 
@@ -493,6 +527,14 @@
     else if( tmpmail.empty() ) tmpmail = JD_MAIL_BLANK; // 空白の場合 JD_MAIL_BLANK をセットする
     DBTREE::board_set_write_mail( get_url(), tmpmail );
 
+    // charset
+#if GTKMM_CHECK_VERSION(2,24,0)
+    std::string tmpcharset = m_combo_charset.get_active_text();
+#else
+    std::string tmpcharset = m_combo_charset.get_entry()->get_text();
+#endif
+    DBTREE::board_set_charcode( get_url(), MISC::charcode_from_cstr( tmpcharset.c_str() ) );
+
     // 実況間隔
     int live_sec = 0;
     if( m_check_live.get_active() ) live_sec = m_spin_live.get_value_as_int();
@@ -517,11 +559,12 @@
     std::list< std::string > list_thread = MISC::get_lines( m_edit_thread.get_text() );
     std::list< std::string > list_word_thread = MISC::get_lines( m_edit_word_thread.get_text() );
     std::list< std::string > list_regex_thread = MISC::get_lines( m_edit_regex_thread.get_text() );
-    const int number = m_spin_number.get_value_as_int();
+    const int min_number = m_spin_min_number.get_value_as_int();
+    const int max_number = m_spin_max_number.get_value_as_int();
     const int hour = m_spin_hour.get_value_as_int();
 
     const bool redraw = true; // ここでスレ一覧の再描画指定をする
-    DBTREE::reset_abone_thread( get_url(), list_thread, list_word_thread, list_regex_thread, number, hour, redraw );  
+    DBTREE::reset_abone_thread( get_url(), list_thread, list_word_thread, list_regex_thread, min_number, max_number, hour, redraw );
 
     DBTREE::board_save_info( get_url() );
 }
diff -aurNZ jd-2.8.9-150226/src/board/preference.h jd-2.8.9-a171004/src/board/preference.h
--- jd-2.8.9-150226/src/board/preference.h	2011-02-20 00:05:44.000000000 +0900
+++ jd-2.8.9-a171004/src/board/preference.h	2016-05-11 02:22:41.978140000 +0900
@@ -77,6 +77,14 @@
         Gtk::CheckButton m_check_live;
         SKELETON::SpinButton m_spin_live;
 
+        // 文字エンコーディング
+        Gtk::Label m_label_charset;
+#if GTKMM_CHECK_VERSION(2,24,0)
+        Gtk::ComboBoxText m_combo_charset;
+#else
+        Gtk::Combo m_combo_charset;
+#endif
+
         // プロキシ
         Gtk::VBox m_vbox_proxy;
         Gtk::Label m_label_proxy;
@@ -124,9 +132,13 @@
         Gtk::VBox m_vbox_abone_thread;
         Gtk::Label m_label_abone_thread;
 
-        Gtk::HBox m_hbox_number;
-        Gtk::Label m_label_number;
-        SKELETON::SpinButton m_spin_number;
+        Gtk::HBox m_hbox_min_number;
+        Gtk::Label m_label_min_number;
+        SKELETON::SpinButton m_spin_min_number;
+
+        Gtk::HBox m_hbox_max_number;
+        Gtk::Label m_label_max_number;
+        SKELETON::SpinButton m_spin_max_number;
 
         Gtk::HBox m_hbox_hour;
         Gtk::Label m_label_hour;
diff -aurNZ jd-2.8.9-150226/src/board/toolbar.cpp jd-2.8.9-a171004/src/board/toolbar.cpp
--- jd-2.8.9-150226/src/board/toolbar.cpp	2011-05-31 00:08:41.000000000 +0900
+++ jd-2.8.9-a171004/src/board/toolbar.cpp	2017-04-05 21:31:33.128433000 +0900
@@ -26,6 +26,11 @@
     add_search_control_mode( CONTROL::MODE_BOARD );
 }
 
+
+BoardToolBar::~BoardToolBar()
+{}
+
+
 // ボタンのパッキング
 // virtual
 void BoardToolBar::pack_buttons()
diff -aurNZ jd-2.8.9-150226/src/board/toolbar.h jd-2.8.9-a171004/src/board/toolbar.h
--- jd-2.8.9-150226/src/board/toolbar.h	2009-08-15 12:25:06.000000000 +0900
+++ jd-2.8.9-a171004/src/board/toolbar.h	2017-04-05 21:31:09.696433000 +0900
@@ -16,7 +16,7 @@
       public:
 
         BoardToolBar();
-        virtual ~BoardToolBar(){}
+        virtual ~BoardToolBar();
 
         // ツールバー表示切り替え時に検索関係の wiget の位置を変更する
         void unpack_pack();
diff -aurNZ jd-2.8.9-150226/src/cache.cpp jd-2.8.9-a171004/src/cache.cpp
--- jd-2.8.9-150226/src/cache.cpp	2013-02-18 00:54:24.000000000 +0900
+++ jd-2.8.9-a171004/src/cache.cpp	2016-01-31 04:30:41.927970000 +0900
@@ -51,15 +51,6 @@
 }
 
 
-// 旧設定ファイル
-std::string CACHE::path_conf_old()
-{
-    std::string home = MISC::getenv_limited( ENV_HOME, MAX_SAFE_PATH );
-
-    return home + "/.jdrc";
-}
-
-
 // セッション情報ファイル
 std::string CACHE::path_session()
 {
@@ -104,7 +95,7 @@
 }
 
 
-// 板リスト
+// 板リスト ( 1.9.5-beta070611以前は "list_main.xml" )
 std::string CACHE::path_xml_listmain()
 {
     return CACHE::path_root() +  "boards.xml";
@@ -114,14 +105,9 @@
 {
     return CACHE::path_xml_listmain() + ".bkup";
 }
-// 1.9.5-beta070611以前
-std::string CACHE::path_xml_listmain_old()
-{
-    return CACHE::path_root() +  "list_main.xml";
-}
 
 
-// お気に入り
+// お気に入り ( 1.9.5-beta070611以前は "favorite.xml" )
 std::string CACHE::path_xml_favorite()
 {
     return CACHE::path_root() +  "bookmark.xml";
@@ -131,11 +117,6 @@
 {
     return CACHE::path_xml_favorite() + ".bkup";
 }
-// 1.9.5-beta070611以前
-std::string CACHE::path_xml_favorite_old()
-{
-    return CACHE::path_root() +  "favorite.xml";
-}
 
 
 // 外部板設定ファイル( navi2ch 互換 )
@@ -151,11 +132,6 @@
     return CACHE::path_root() +  "usrcmd.xml";
 }
 
-std::string CACHE::path_usrcmd_old()
-{
-    return CACHE::path_root() +  "usrcmd.txt";
-}
-
 
 // リンクフィルタ設定ファイル
 std::string CACHE::path_linkfilter()
@@ -164,6 +140,13 @@
 }
 
 
+// 文字列置換設定ファイル
+std::string CACHE::path_replacestr()
+{
+    return CACHE::path_root() +  "replacestr.xml";
+}
+
+
 // URL変換設定ファイル
 std::string CACHE::path_urlreplace()
 {
diff -aurNZ jd-2.8.9-150226/src/cache.h jd-2.8.9-a171004/src/cache.h
--- jd-2.8.9-150226/src/cache.h	2013-02-18 00:54:24.000000000 +0900
+++ jd-2.8.9-a171004/src/cache.h	2016-01-31 04:29:55.015970000 +0900
@@ -43,7 +43,6 @@
     // 設定ファイル
     std::string path_conf();
     std::string path_conf_bkup();
-    std::string path_conf_old();  // 旧ファイル
 
     // セッション情報ファイル
     std::string path_session();
@@ -60,23 +59,23 @@
     // 板
     std::string path_xml_listmain();
     std::string path_xml_listmain_bkup();
-    std::string path_xml_listmain_old();
 
     // お気に入り
     std::string path_xml_favorite();
     std::string path_xml_favorite_bkup();
-    std::string path_xml_favorite_old();
 
     // 外部板設定ファイル( navi2ch 互換 )
     std::string path_etcboard();
 
     // ユーザーコマンド設定ファイル
     std::string path_usrcmd();
-    std::string path_usrcmd_old();
 
     // リンクフィルタ
     std::string path_linkfilter();
 
+    // 文字列置換
+    std::string path_replacestr();
+
     // URL変換設定ファイル
     std::string path_urlreplace();
 
diff -aurNZ jd-2.8.9-150226/src/charcode.h jd-2.8.9-a171004/src/charcode.h
--- jd-2.8.9-150226/src/charcode.h	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/charcode.h	2016-01-04 23:12:19.271275000 +0900
@@ -0,0 +1,16 @@
+// キャラクタコード
+
+#ifndef _CHAR_CODE_H
+#define _CHAR_CODE_H
+
+enum CharCode
+{
+    CHARCODE_UNKNOWN = 0,
+    CHARCODE_ASCII,
+    CHARCODE_EUCJP,
+    CHARCODE_JIS,
+    CHARCODE_SJIS,
+    CHARCODE_UTF8
+};
+
+#endif
diff -aurNZ jd-2.8.9-150226/src/config/aboutconfig.cpp jd-2.8.9-a171004/src/config/aboutconfig.cpp
--- jd-2.8.9-150226/src/config/aboutconfig.cpp	2014-04-27 02:06:18.000000000 +0900
+++ jd-2.8.9-a171004/src/config/aboutconfig.cpp	2017-10-04 15:15:59.110150502 +0900
@@ -49,6 +49,7 @@
     m_label.set_text( "動作保証外です。高度な設定を変更するとJDが誤作動する場合があります。" );
 
     m_liststore = Gtk::ListStore::create( m_columns );
+    m_treeview.property_fixed_height_mode() = true;
     m_treeview.set_model( m_liststore );
     m_treeview.set_size_request( 700, 400 );
     m_treeview.signal_row_activated().connect( sigc::mem_fun( *this, &AboutConfig::slot_row_activated ) );
@@ -62,6 +63,8 @@
     if( cell ) column->set_cell_data_func( *cell, sigc::mem_fun( *this, &AboutConfig::slot_cell_data ) );
 
     column = Gtk::manage( new Gtk::TreeViewColumn( "値", m_columns.m_col_value ) );
+    column->set_sizing( Gtk::TREE_VIEW_COLUMN_FIXED );
+    column->set_resizable( true );
     m_treeview.append_column( *column );
     cell = column->get_first_cell_renderer();
     if( cell ) column->set_cell_data_func( *cell, sigc::mem_fun( *this, &AboutConfig::slot_cell_data ) );
@@ -120,12 +123,21 @@
     append_row( "■ ネットワーク" );
     append_row(	"JD ホームページのアドレス", get_confitem()->url_jdhp, CONF_URL_JDHP );
     append_row(	"板一覧を取得するサーバ", get_confitem()->url_bbsmenu, CONF_URL_BBSMENU );
-    append_row( "2chログイン認証サーバのアドレス", get_confitem()->url_login2ch, CONF_LOGIN2CH );
+    append_row( "5ch APIサーバのアドレス", get_confitem()->url_api2ch, CONF_API2CH );
     append_row( "p2ログイン認証サーバのアドレス", get_confitem()->url_loginp2, CONF_LOGINP2 );
     append_row( "BE認証サーバのアドレス", get_confitem()->url_loginbe, CONF_LOGINBE );
-    append_row( "2chにアクセスするときのエージェント名", get_confitem()->agent_for2ch, CONF_AGENT_FOR2CH );
-    append_row( "2ch以外のサーバにアクセスするときのエージェント名", get_confitem()->agent_for_data, CONF_AGENT_FOR_DATA );
-    append_row( "2chログイン認証サーバにアクセスするときのエージェント名", get_confitem()->x_2ch_ua, CONF_X_2CH_UA );
+    append_row( "通常のアクセスを行う5chサーバ( スペース区切り )", get_confitem()->dedicated_server, CONF_DEDICATED_SERVER );
+    append_row( "5chにアクセスするときのエージェント名", get_confitem()->agent_for2ch, CONF_AGENT_FOR2CH );
+    append_row( "5chのread.cgiにアクセスするときのエージェント名", get_confitem()->agent_for_readcgi, CONF_AGENT_FOR_READCGI );
+    append_row( "5ch以外のサーバにアクセスするときのエージェント名", get_confitem()->agent_for_data, CONF_AGENT_FOR_DATA );
+    append_row( "5ch APIサーバにアクセスするときの拡張エージェント名", get_confitem()->x_2ch_ua, CONF_X_2CH_UA );
+    append_row( "5chの認証に使用するAppKey", get_confitem()->appkey, CONF_APPKEY );
+    append_row( "5chの認証に使用するHMKey", get_confitem()->hmkey, CONF_HMKEY );
+    append_row( "TLSでノンブロッキングI/Oを使用する", get_confitem()->tls_nonblocking, CONF_TLS_NONBLOCKING );
+    append_row( "TLSでサーバの証明書をチェックする", get_confitem()->verify_cert, CONF_VERIFY_CERT );
+#ifndef USE_NSS
+    append_row( "信頼するルート証明書のPATH", get_confitem()->root_cafile, CONF_ROOT_CAFILE );
+#endif
     append_row( "スレの読み込み時のタイムアウト値(秒)", get_confitem()->loader_timeout, CONF_LOADER_TIMEOUT );
     append_row( "書き込み時のタイムアウト値(秒)", get_confitem()->loader_timeout_post, CONF_LOADER_TIMEOUT_POST );
     append_row( "画像等のデータのロード時のタイムアウト値(秒)", get_confitem()->loader_timeout_img, CONF_LOADER_TIMEOUT_IMG );
@@ -133,10 +145,12 @@
     append_row( "一般データのダウンロード時のバッファサイズ(Kbyte)", get_confitem()->loader_bufsize, CONF_LOADER_BUFSIZE );
     append_row( "スレ一覧のダウンロード時のバッファサイズ(Kbyte)", get_confitem()->loader_bufsize_board, CONF_LOADER_BUFSIZE_BOARD );
     append_row( "同一ホストに対する最大コネクション数( 1 または 2 )", get_confitem()->connection_num, CONF_CONNECTION_NUM );
-    append_row( "2chのクッキー:HAPを保存する", get_confitem()->use_cookie_hap, CONF_USE_COOKIE_HAP );
-    append_row( "2chのクッキー:HAP", get_confitem()->cookie_hap, CONF_COOKIE_HAP );
+    append_row( "5chのクッキー:HAPを保存する", get_confitem()->use_cookie_hap, CONF_USE_COOKIE_HAP );
+    append_row( "5chのクッキー:HAP", get_confitem()->cookie_hap, CONF_COOKIE_HAP );
     append_row( "BBSPINKのクッキー:HAP", get_confitem()->cookie_hap_bbspink, CONF_COOKIE_HAP_BBSPINK );
-    append_row( "2chの過去ログ取得時にofflaw2を使用する", get_confitem()->use_offlaw2_2ch, CONF_USE_OFFLAW2_2CH );
+    append_row( "5chの過去ログ取得時にread.cgiを使用する", get_confitem()->use_2chkako_readcgi, CONF_USE_2CHKAKO_READCGI );
+    append_row( "5chの過去ログを外部のサイトから取得する", get_confitem()->use_external_log, CONF_USE_EXTERNAL_LOG );
+    append_row( "5chの過去ログを取得するサイトのURL", get_confitem()->url_external_log, CONF_URL_EXTERNAL_LOG );
 
     // ツリービュー
     append_row( "" );
@@ -204,6 +218,7 @@
     append_row( "レスを引用コピーするときに前に付ける引用文字", get_confitem()->ref_prefix, CONF_REF_PREFIX );
     append_row( "引用文字の後のスペース数", get_confitem()->ref_prefix_space, CONF_REF_PREFIX_SPACE );
     append_row( "RFC規定外の文字(^など)もURL判定に用いる", get_confitem()->loose_url, CONF_LOOSE_URL );
+    append_row( "URLのパーセントコードをデコードして表示する", get_confitem()->percent_decode, CONF_PERCENT_DECODE );
     append_row( "再読み込みボタンを押したときに全タブを更新する", get_confitem()->reload_allthreads, CONF_RELOAD_ALLTHREAD );
     append_row( "発言(同一ID)数をカウントする", get_confitem()->check_id, CONF_CHECK_ID );
     append_row( "レス参照数で色を変える回数(高)", get_confitem()->num_reference_high, CONF_NUM_REFERENCE_HIGH );
@@ -261,6 +276,7 @@
     append_row( "スレビューのスクロールバーを左に配置する", get_confitem()->left_scrbar, CONF_LEFT_SCRBAR );
     append_row( "メニューバーを非表示にした時にダイアログを表示", get_confitem()->show_hide_menubar_diag, CONF_SHOW_HIDE_MENUBAR_DIAG );
     append_row( "状態変更時にメインステータスバーの色を変える", get_confitem()->change_stastatus_color, CONF_CHANGE_STASTATUS_COLOR );
+    append_row( "状態変更時にスレビュータイトルの色を変える", get_confitem()->change_statitle_color, CONF_CHANGE_STATITLE_COLOR );
 
     // 次スレ検索
     append_row( "" );
@@ -292,6 +308,8 @@
 #endif
     append_row( "FIFOの作成などにエラーがあったらダイアログを表示する", get_confitem()->show_diag_fifo_error, CONF_SHOW_DIAG_FIFO_ERROR );
     append_row( "指定した分ごとにセッションを自動保存 (0: 保存しない)", get_confitem()->save_session, CONF_SAVE_SESSION );
+    append_row( "不正なMS932文字列をUTF-8と見なす", get_confitem()->broken_sjis_be_utf8, CONF_BROKEN_SJIS_BE_UTF8 );
+    append_row( "不正な数値文字参照を無理矢理変換する", get_confitem()->correct_character_reference, CONF_CORRECT_CHAR_REFERENCE );
 }
 
 
diff -aurNZ jd-2.8.9-150226/src/config/configitems.cpp jd-2.8.9-a171004/src/config/configitems.cpp
--- jd-2.8.9-150226/src/config/configitems.cpp	2014-03-29 15:36:22.000000000 +0900
+++ jd-2.8.9-a171004/src/config/configitems.cpp	2017-03-22 18:25:11.020044000 +0900
@@ -75,22 +75,8 @@
     std::string path_conf;
 
     // restoreモード
-    if( restore )
-    {
-        path_conf = CACHE::path_conf_bkup();
-    }
-    else
-    {
-        path_conf = CACHE::path_conf();
-
-        // 新設定ファイルが無く、かつキャッシュディレクトリが存在していたら旧ファイルから設定を引き継ぐ
-        if( CACHE::file_exists( path_conf ) != CACHE::EXIST_FILE
-            && CACHE::file_exists( CACHE::path_root() ) == CACHE::EXIST_DIR
-            && CACHE::file_exists( CACHE::path_conf_old() ) == CACHE::EXIST_FILE ){
-
-                path_conf = CACHE::path_conf_old();
-        }
-    }
+    if( restore ) path_conf = CACHE::path_conf_bkup();
+    else path_conf = CACHE::path_conf();
 
     JDLIB::ConfLoader cf( path_conf, std::string() );
 
@@ -150,12 +136,21 @@
     // 2ch にアクセスするときのエージェント名
     agent_for2ch = cf.get_option_str( "agent_for2ch", CONF_AGENT_FOR2CH );
 
+    // 2chのread.cgiにアクセスするときのエージェント名
+    agent_for_readcgi = cf.get_option_str( "agent_for_readcgi", CONF_AGENT_FOR_READCGI );
+
     // 2ch外にアクセスするときのエージェント名
     agent_for_data = cf.get_option_str( "agent_for_data", CONF_AGENT_FOR_DATA );
 
-    // 2ch にログインするときのX-2ch-UA
+    // 2ch APIサーバにアクセスするときのX-2ch-UA
     x_2ch_ua = cf.get_option_str( "x_2ch_ua", CONF_X_2CH_UA );
 
+    // 2chの認証に使用するAPPKEY
+    appkey = cf.get_option_str( "appkey", CONF_APPKEY );
+
+    // 2chの認証に使用するHMKEY
+    hmkey = cf.get_option_str( "hmkey", CONF_HMKEY );
+
     // ローダのバッファサイズ
     loader_bufsize = cf.get_option_int( "loader_bufsize", CONF_LOADER_BUFSIZE, 1, 4096 ); // 一般
     loader_bufsize_board = cf.get_option_int( "loader_bufsize_board", CONF_LOADER_BUFSIZE_BOARD, 1, 4096 ); // スレ一覧用
@@ -169,6 +164,15 @@
     // ipv6使用
     use_ipv6 = cf.get_option_bool( "use_ipv6", CONF_USE_IPV6 );
 
+    // TLSでノンブロッキングI/Oを使用する
+    tls_nonblocking = cf.get_option_bool( "tls_nonblocking", CONF_TLS_NONBLOCKING );
+
+    // TLSでサーバの証明書をチェックする
+    verify_cert = cf.get_option_bool( "verify_cert", CONF_VERIFY_CERT );
+
+    // 信頼するルート証明書
+    root_cafile = cf.get_option_str( "root_cafile", CONF_ROOT_CAFILE );
+
     // 同一ホストに対する最大コネクション数( 1 または 2 )
     connection_num = cf.get_option_int( "connection_num", CONF_CONNECTION_NUM, 1, 2 );
 
@@ -179,8 +183,12 @@
     cookie_hap = cf.get_option_str( "cookie_hap", CONF_COOKIE_HAP );
     cookie_hap_bbspink = cf.get_option_str( "cookie_hap_bbspink", CONF_COOKIE_HAP_BBSPINK );
 
-    // 2chの過去ログ取得時にofflaw2を使用する
-    use_offlaw2_2ch = cf.get_option_bool( "use_offlaw2_2ch", CONF_USE_OFFLAW2_2CH );
+    // 2chの過去ログ取得時にread.cgiを使用する
+    use_2chkako_readcgi = cf.get_option_bool( "use_2chkako_readcgi", CONF_USE_2CHKAKO_READCGI );
+
+    // 2chの過去ログを外部のサイトから取得する
+    use_external_log = cf.get_option_bool( "use_external_log", CONF_USE_EXTERNAL_LOG );
+    url_external_log = cf.get_option_str( "url_external_log", CONF_URL_EXTERNAL_LOG );
 
     // ブラウザ設定ダイアログのコンボボックスの番号
     browsercombo_id = cf.get_option_int( "browsercombo_id", CONF_BROWSER_NO, 0, CORE::get_browser_number() -1 );
@@ -255,8 +263,8 @@
     // JD ホームページのアドレス
     url_jdhp = cf.get_option_str( "url_jdhp", CONF_URL_JDHP );
 
-    // 2chの認証サーバのアドレス
-    url_login2ch = cf.get_option_str( "url_login2ch", CONF_LOGIN2CH );
+    // 2ch APIサーバのアドレス
+    url_api2ch = cf.get_option_str( "url_api2ch", CONF_API2CH );
 
     // p2の認証サーバのアドレス
     url_loginp2 = cf.get_option_str( "url_loginp2", CONF_LOGINP2 );
@@ -264,6 +272,9 @@
     // BEの認証サーバのアドレス
     url_loginbe = cf.get_option_str( "url_loginbe", CONF_LOGINBE );
 
+    // 通常のアクセスを行う2chサーバ
+    dedicated_server = cf.get_option_str( "dedicated_server", CONF_DEDICATED_SERVER );
+
     // bbsmenu.htmlのURL
     url_bbsmenu = cf.get_option_str( "url_bbsmenu", CONF_URL_BBSMENU );
 
@@ -277,10 +288,6 @@
     menu_search_title = cf.get_option_str( "menu_search_title", CONF_MENU_SEARCH_TITLE );
     url_search_title = cf.get_option_str( "url_search_title", CONF_URL_SEARCH_TITLE );
 
-    // p2 書き込み用アドレス
-    url_writep2 = cf.get_option_str( "url_writep2", CONF_URL_WRITEP2 );
-    url_resp2 = cf.get_option_str( "url_resp2", CONF_URL_RESP2 );
-
     // スレタイ検索用正規表現
     regex_search_title = cf.get_option_str( "regex_search_title", CONF_REGEX_SEARCH_TITLE );
 
@@ -294,6 +301,9 @@
     // スレビューの選択色でgtkrcの設定を使用するか
     use_select_gtkrc = cf.get_option_bool( "use_select_gtkrc", CONF_USE_SELECT_GTKRC );
 
+    // スレビューでHTMLタグ指定の色を使用するか
+    use_color_html = cf.get_option_bool( "use_color_html", CONF_USE_COLOR_HTML );
+
     // ツリービューの行間スペース
     tree_ypad = cf.get_option_int( "tree_ypad", CONF_TREE_YPAD, 0, 64 );
 
@@ -470,6 +480,9 @@
     // datのパース時にURL判定を甘くする(^なども含める)
     loose_url = cf.get_option_bool( "loose_url", CONF_LOOSE_URL );
 
+    // URLのパーセントコードをデコードして表示する
+    percent_decode = cf.get_option_bool( "percent_decode", CONF_PERCENT_DECODE );
+
     // ユーザーコマンドで選択できない項目を非表示にする
     hide_usrcmd = cf.get_option_bool( "hide_usrcmd", CONF_HIDE_USRCMD );
 
@@ -518,7 +531,8 @@
     remove_old_abone_thread = cf.get_option_int( "remove_old_abone_thread", CONF_REMOVE_OLD_ABONE_THREAD, 0, 2 );
 
     // スレ あぼーん( レス数 )
-    abone_number_thread = cf.get_option_int( "abone_number_thread", CONF_ABONE_NUMBER_THREAD, 0, 9999 );
+    abone_min_number_thread = cf.get_option_int( "abone_min_number_thread", CONF_ABONE_MIN_NUMBER_THREAD, 0, MAX_RESNUMBER );
+    abone_max_number_thread = cf.get_option_int( "abone_number_thread", CONF_ABONE_MAX_NUMBER_THREAD, 0, MAX_RESNUMBER );
 
     // スレ あぼーん( スレ立てからの経過時間 )
     abone_hour_thread = cf.get_option_int( "abone_hour_thread", CONF_ABONE_HOUR_THREAD, 0, 9999 );
@@ -585,6 +599,12 @@
     // 状態変更時にメインステータスバーの色を変える
     change_stastatus_color = cf.get_option_bool( "change_stastatus_color", CONF_CHANGE_STASTATUS_COLOR );
 
+    // 状態変更時にスレビュータイトルの色を変える
+    change_statitle_color = cf.get_option_bool( "change_statitle_color", CONF_CHANGE_STATITLE_COLOR );
+
+    // 2chのスレ取得に read.cgi を使用する
+    use_2ch_readcgi = cf.get_option_bool( "use_2ch_readcgi", CONF_USE_2CH_READCGI );
+
     // まちBBSの取得に offlaw.cgi を使用する
     use_machi_offlaw = cf.get_option_bool( "use_machi_offlaw", CONF_USE_MACHI_OFFLAW );
 
@@ -605,6 +625,12 @@
     migemodict_path = cf.get_option_str( "migemodict_path", CONF_MIGEMO_PATH );
 #endif
 
+    // 不正なMS932文字列をUTF-8と見なす
+    broken_sjis_be_utf8 = cf.get_option_bool( "broken_sjis_be_utf8", CONF_BROKEN_SJIS_BE_UTF8 );
+
+    // 不正な数値文字参照を無理矢理変換する
+    correct_character_reference = cf.get_option_bool( "correct_character_reference", CONF_CORRECT_CHAR_REFERENCE );
+
     m_loaded = true;
 
     // 設定値に壊れている物がある
@@ -644,7 +670,6 @@
 void ConfigItems::save()
 {
     save_impl( CACHE::path_conf() );
-    if( CACHE::file_exists( CACHE::path_conf_old() ) == CACHE::EXIST_FILE ) save_impl( CACHE::path_conf_old() );
 }
 
 
@@ -663,9 +688,10 @@
     cf.update( "restore_image", restore_image );
     cf.update( "manage_winpos", manage_winpos );
     cf.update( "url_jdhp", url_jdhp );
-    cf.update( "url_login2ch", url_login2ch );
+    cf.update( "url_api2ch", url_api2ch );
     cf.update( "url_loginp2", url_loginp2 );
     cf.update( "url_loginbe", url_loginbe );
+    cf.update( "dedicated_server", dedicated_server );
     cf.update( "url_bbsmenu", url_bbsmenu );
     cf.update( "use_link_as_board", use_link_as_board );
     cf.update( "show_movediag", show_movediag );
@@ -674,8 +700,6 @@
     cf.update( "regex_search_title", regex_search_title );
     cf.update( "menu_search_web", menu_search_web );
     cf.update( "url_search_web", url_search_web );
-    cf.update( "url_writep2", url_writep2 );
-    cf.update( "url_resp2", url_resp2 );
 
     cf.update( "fontname_main", fontname[ FONT_MAIN ] );
     cf.update( "fontname_popup", fontname[ FONT_POPUP ] );
@@ -692,6 +716,7 @@
     cf.update( "path_cacheroot", path_cacheroot );
 
     cf.update( "agent_for2ch", agent_for2ch );
+    cf.update( "agent_for_readcgi", agent_for_readcgi );
 
     std::string tmp_proxy;
     if( proxy_basicauth_for2ch.empty() ) tmp_proxy = proxy_for2ch;
@@ -715,6 +740,8 @@
     cf.update( "proxy_port_for_data", proxy_port_for_data );
 
     cf.update( "x_2ch_ua", x_2ch_ua );
+    cf.update( "appkey", appkey);
+    cf.update( "hmkey", hmkey );
 
     cf.update( "loader_bufsize", loader_bufsize );
     cf.update( "loader_bufsize_board", loader_bufsize_board );
@@ -725,13 +752,19 @@
     cf.update( "loader_timeout_checkupdate", loader_timeout_checkupdate );
 
     cf.update( "use_ipv6", use_ipv6 );
+    cf.update( "tls_nonblocking", tls_nonblocking );
+    cf.update( "verify_cert", verify_cert );
+    cf.update( "root_cafile", root_cafile );
     cf.update( "connection_num", connection_num );
 
     cf.update( "use_cookie_hap", use_cookie_hap );
     cf.update( "cookie_hap", cookie_hap );
     cf.update( "cookie_hap_bbspink", cookie_hap_bbspink );
 
-    cf.update( "use_offlaw2_2ch", use_offlaw2_2ch );
+    cf.update( "use_2chkako_readcgi", use_2chkako_readcgi );
+
+    cf.update( "use_external_log", use_external_log );
+    cf.update( "url_external_log", url_external_log );
 
     cf.update( "command_openurl", command_openurl );
     cf.update( "browsercombo_id", browsercombo_id );
@@ -801,6 +834,7 @@
 
     cf.update( "use_tree_gtkrc", use_tree_gtkrc );
     cf.update( "use_select_gtkrc", use_select_gtkrc );
+    cf.update( "use_color_html", use_color_html );
 
     cf.update( "tree_ypad", tree_ypad );
     cf.update( "tree_show_expanders", tree_show_expanders );
@@ -875,6 +909,8 @@
 
     cf.update( "loose_url", loose_url );
 
+    cf.update( "percent_decode", percent_decode );
+
     cf.update( "hide_usrcmd", hide_usrcmd );
     cf.update( "reload_allthreads", reload_allthreads );
 
@@ -904,7 +940,8 @@
 
     cf.update( "remove_old_abone_thread", remove_old_abone_thread );
 
-    cf.update( "abone_number_thread", abone_number_thread );
+    cf.update( "abone_min_number_thread", abone_min_number_thread );
+    cf.update( "abone_number_thread", abone_max_number_thread );
     cf.update( "abone_hour_thread", abone_hour_thread );
 
     // あぼーん情報
@@ -938,6 +975,8 @@
     cf.update( "disable_close", disable_close );
     cf.update( "show_hide_menubar_diag", show_hide_menubar_diag );
     cf.update( "change_stastatus_color", change_stastatus_color );
+    cf.update( "change_statitle_color", change_statitle_color );
+    cf.update( "use_2ch_readcgi", use_2ch_readcgi );
     cf.update( "use_machi_offlaw", use_machi_offlaw );
     cf.update( "show_del_written_thread_diag", show_del_written_thread_diag );
     cf.update( "delete_img_in_thread", delete_img_in_thread );
@@ -948,6 +987,9 @@
     cf.update( "migemodict_path", migemodict_path );
 #endif
 
+    cf.update( "broken_sjis_be_utf8", broken_sjis_be_utf8 );
+    cf.update( "correct_character_reference", correct_character_reference );
+
     cf.save();
 }
 
diff -aurNZ jd-2.8.9-150226/src/config/configitems.h jd-2.8.9-a171004/src/config/configitems.h
--- jd-2.8.9-150226/src/config/configitems.h	2014-03-29 15:36:22.000000000 +0900
+++ jd-2.8.9-a171004/src/config/configitems.h	2016-05-11 02:55:02.462110000 +0900
@@ -72,12 +72,21 @@
         // 2ch にアクセスするときのエージェント名
         std::string agent_for2ch;
 
+        // 2chのread.cgiにアクセスするときのエージェント名
+        std::string agent_for_readcgi;
+
         // 2ch外にアクセスするときのエージェント名
         std::string agent_for_data;
 
-        // 2ch にログインするときのX-2ch-UA
+        // 2ch APIサーバにアクセスするときのX-2ch-UA
         std::string x_2ch_ua;
 
+        // 2chの認証に使用するAPPKEY
+        std::string appkey;
+
+        // 2chの認証に使用するHMKEY
+        std::string hmkey;
+
         // ローダのバッファサイズ
         int loader_bufsize; // 一般
         int loader_bufsize_board; // スレ一覧用
@@ -91,6 +100,15 @@
         // ipv6使用
         bool use_ipv6;
 
+        // TLSでノンブロッキングI/Oを使用する
+        bool tls_nonblocking;
+
+        // TLSでサーバの証明書をチェックする
+        bool verify_cert;
+
+        // 信頼するルート証明書
+        std::string root_cafile;
+
         // 同一ホストに対する最大コネクション数( 1 または 2 )
         int connection_num;
 
@@ -101,8 +119,12 @@
         std::string cookie_hap;
         std::string cookie_hap_bbspink;
 
-        // 2chの過去ログ取得時にofflaw2を使用する
-        bool use_offlaw2_2ch;
+        // 2chの過去ログ取得時にread.cgiを使用する
+        bool use_2chkako_readcgi;
+
+        // 2chの過去ログを外部のサイトから取得する
+        bool use_external_log;
+        std::string url_external_log;
 
         // リンクをクリックしたときに実行するコマンド
         std::string command_openurl;
@@ -179,8 +201,8 @@
         // JD ホームページのアドレス
         std::string url_jdhp;
 
-        // 2chの認証サーバのアドレス
-        std::string url_login2ch;
+        // 2ch APIサーバのアドレス
+        std::string url_api2ch;
 
         // p2の認証サーバのアドレス
         std::string url_loginp2;
@@ -188,6 +210,9 @@
         // BEの認証サーバのアドレス
         std::string url_loginbe;
 
+        // 通常のアクセスを行う2chサーバ
+        std::string dedicated_server;
+
         // bbsmenu.htmlのURL
         std::string url_bbsmenu;
 
@@ -208,10 +233,6 @@
         std::string menu_search_web;
         std::string url_search_web;
 
-        // p2 書き込み用アドレス
-        std::string url_writep2;
-        std::string url_resp2;
-
         // 色
         std::vector< std::string > str_color;
 
@@ -221,6 +242,9 @@
         // スレビューの選択色でgtkrcの設定を使用するか
         bool use_select_gtkrc;
 
+        // スレビューでHTMLタグ指定の色を使用するか
+        bool use_color_html;
+
         // ツリービューの行間スペース
         int tree_ypad;
 
@@ -397,6 +421,9 @@
         // datのパース時にURL判定を甘くする(^なども含める)
         bool loose_url;
 
+        // URLのパーセントコードをデコードして表示する
+        bool percent_decode;
+
         // ユーザーコマンドで選択できない項目を非表示にする
         bool hide_usrcmd;
 
@@ -440,7 +467,8 @@
         int remove_old_abone_thread;
 
         // スレ あぼーん レス数
-        int abone_number_thread;
+        int abone_min_number_thread;
+        int abone_max_number_thread;
 
         // スレ あぼーん スレ立てからの経過時間
         int abone_hour_thread;
@@ -504,6 +532,12 @@
         // 状態変更時にメインステータスバーの色を変える
         bool change_stastatus_color;
 
+        // 状態変更時にスレビュータイトルの色を変える
+        bool change_statitle_color;
+
+        // 2chのスレ取得に read.cgi を使用する
+        bool use_2ch_readcgi;
+
         // まちBBSの取得に offlaw.cgi を使用する
         bool use_machi_offlaw;
 
@@ -524,6 +558,12 @@
         std::string migemodict_path;
 #endif
 
+        // 不正なMS932文字列をUTF-8と見なす
+        bool broken_sjis_be_utf8;
+
+        // 不正な数値文字参照を無理矢理変換する
+        bool correct_character_reference;
+
         /////////////////////////
 
 
diff -aurNZ jd-2.8.9-150226/src/config/defaultconf.h jd-2.8.9-a171004/src/config/defaultconf.h
--- jd-2.8.9-150226/src/config/defaultconf.h	2014-11-23 22:52:14.000000000 +0900
+++ jd-2.8.9-a171004/src/config/defaultconf.h	2017-10-04 15:13:22.050152907 +0900
@@ -35,14 +35,18 @@
         CONF_LOADER_TIMEOUT_IMG = 30,  // 画像ローダのタイムアウト値
         CONF_LOADER_TIMEOUT_CHECKUPDATE = 10,  // 更新チェックのタイムアウト値
         CONF_USE_IPV6 = 1,          // ipv6使用
+        CONF_TLS_NONBLOCKING = 1,   // TLSでノンブロッキングI/Oを使用する
+        CONF_VERIFY_CERT = 1,       // TLSでサーバの証明書をチェックする
         CONF_CONNECTION_NUM = 2,    // 同一ホストに対する最大コネクション数( 1 または 2 )
         CONF_USE_COOKIE_HAP = 0,    // 2chのクッキー:HAPを保存する
-        CONF_USE_OFFLAW2_2CH = 0,   // 2chの過去ログ取得時にofflaw2を使用する
+        CONF_USE_2CHKAKO_READCGI = 0,   // 2chの過去ログ取得時にread.cgiを使用する
+        CONF_USE_EXTERNAL_LOG = 0,  // 2chの過去ログを外部サイトから取得する
         CONF_REFPOPUP_BY_MO = 0,    // レス番号の上にマウスオーバーしたときに参照ポップアップ表示する
         CONF_NAMEPOPUP_BY_MO = 0,   // 名前の上にマウスオーバーしたときにポップアップ表示する
         CONF_IDPOPUP_BY_MO = 0,     // IDの上にマウスオーバーしたときにIDをポップアップ表示する
         CONF_USE_TREE_GTKRC = 0,    // ツリービューでgtkrcの設定を使用するか
         CONF_USE_SELECT_GTKRC = 0,  // スレビューの選択色でgtkrcの設定を使用するか
+        CONF_USE_COLOR_HTML = 1,  // スレビューでHTMLタグ指定の色を使用するか
         CONF_TREE_YPAD = 1,         // ツリービューの行間スペース
         CONF_TREE_SHOW_EXPANDERS = 1, // ツリービューにエクスパンダを表示
         CONF_TREE_LEVEL_INDENT = 0, // ツリービューのレベルインデント調整量(ピクセル)
@@ -100,6 +104,7 @@
         CONF_NUM_ID_HIGH = 4,       // 発言数で色を変える回数 (高)
         CONF_NUM_ID_LOW = 2,        // 発言数で色を変える回数 (低)
         CONF_LOOSE_URL = 1,         // datのパース時にURL判定を甘くする(^なども含める)
+        CONF_PERCENT_DECODE = 0,    // URLのパーセントコードをデコードして表示する
         CONF_HIDE_USRCMD = 0, // ユーザーコマンドで選択できない項目を非表示にする
         CONF_RELOAD_ALLTHREAD = 0,  // スレビューで再読み込みボタンを押したときに全タブを更新する
         CONF_TAB_MIN_STR = 4, // タブに表示する文字列の最小値
@@ -135,7 +140,8 @@
         CONF_USE_LINK_AS_BOARD = 0,     // bbsmenu.html内にあるリンクは全て板とみなす
         CONF_SHOW_MOVEDIAG = 1,    // 板移転時に確認ダイアログを表示する
         CONF_REMOVE_OLD_ABONE_THREAD = 0, // dat落ちしたスレをNGスレタイトルリストから取り除くか( 0: ダイアログ表示 1: 取り除く 2: 除かない )
-        CONF_ABONE_NUMBER_THREAD = 0, // スレあぼーん( レス数 )
+        CONF_ABONE_MIN_NUMBER_THREAD = 0, // スレあぼーん( レス数(最小) )
+        CONF_ABONE_MAX_NUMBER_THREAD = 0, // スレあぼーん( レス数(最大) )
         CONF_ABONE_HOUR_THREAD = 0,   // スレあぼーん( スレ立てからの経過時間 )
         CONF_ABONE_TRANSPARENT = 0, // デフォルトで透明あぼーんをする
         CONF_ABONE_CHAIN = 0,       // デフォルトで連鎖あぼーんをする
@@ -155,11 +161,15 @@
         CONF_DISABLE_CLOSE = 0, // Ctrl+qでウィンドウを閉じない
         CONF_SHOW_HIDE_MENUBAR_DIAG = 1, // メニューバーを非表示にした時にダイアログを表示
         CONF_CHANGE_STASTATUS_COLOR = 1, // 状態変更時にメインステータスバーの色を変える
+        CONF_CHANGE_STATITLE_COLOR = 1, // 状態変更時にスレビュータイトルの色を変える
+        CONF_USE_2CH_READCGI = 0, // 2chのスレ取得に read.cgi を使用する
         CONF_USE_MACHI_OFFLAW = 0, // まちBBSの取得に offlaw.cgi を使用する
         CONF_SHOW_DEL_WRITTEN_THREAD_DIAG = 1, // 書き込み履歴のあるスレを削除する時にダイアログを表示
         CONF_DELETE_IMG_IN_THREAD = 0, // スレを削除する時に画像キャッシュも削除する ( 0: ダイアログ表示 1: 削除 2: 削除しない )
         CONF_SHOW_DIAG_FIFO_ERROR = 1, // FIFOの作成などにエラーがあったらダイアログを表示する
         CONF_SAVE_SESSION = 0, // 指定した分ごとにセッションを自動保存 (0: 保存しない)
+        CONF_BROKEN_SJIS_BE_UTF8 = 0, // 不正なMS932文字列をUTF-8と見なす
+        CONF_CORRECT_CHAR_REFERENCE = 0, // 不正な数値文字参照を無理矢理変換する
     };
 
 // browsers.cpp のデフォルトのラベル番号
@@ -192,49 +202,59 @@
 #define CONF_PATH_CACHEROOT "~/.jd/"
 
 // 2ch にアクセスするときのエージェント名
-#define CONF_AGENT_FOR2CH "Monazilla/1.00 JD"
+#define CONF_AGENT_FOR2CH "Monazilla/1.00 JD/2.9"
+
+// 2chのread.cgiにアクセスするときのエージェント名
+#define CONF_AGENT_FOR_READCGI "Monazilla/1.00 JD/2.9"
 
 // 2ch外にアクセスするときのエージェント名
-#define CONF_AGENT_FOR_DATA "Monazilla/1.00 JD"
+#define CONF_AGENT_FOR_DATA "Monazilla/1.00 JD/2.9"
+
+// 2ch APIサーバにアクセスするときの拡張エージェント名
+#define CONF_X_2CH_UA ""
+
+// 2chのAPPKEY
+#define CONF_APPKEY ""
 
-// 2ch にログインするときのX-2ch-UA
-#define CONF_X_2CH_UA "Navigator for 2ch 1.7.5"
+// 2chのHMKEY
+#define CONF_HMKEY ""
 
 // JD ホームページのアドレス
-#define CONF_URL_JDHP "http://jd4linux.sourceforge.jp/"
+#define CONF_URL_JDHP "http://jd4linux.osdn.jp/"
 
-// 2chの認証サーバのアドレス
-#define CONF_LOGIN2CH "https://2chv.tora3.net/futen.cgi"
+// 2ch APIサーバのアドレス
+#define CONF_API2CH "https://api.5ch.net"
 
 // p2の認証サーバのアドレス
 #define CONF_LOGINP2 "http://p2.2ch.sc/p2/"
 
 // BEの認証サーバのアドレス
-#define CONF_LOGINBE "http://be.2ch.net/test/login.php"
+#define CONF_LOGINBE "http://be.5ch.net/test/login.php"
+
+// 通常のアクセスを行う2chサーバ
+#define CONF_DEDICATED_SERVER "headline.5ch.net qb5.5ch.net qb7.5ch.net"
 
 // bbsmenu.htmlのURL
-#define CONF_URL_BBSMENU "http://menu.2ch.net/bbsmenu.html"
+#define CONF_URL_BBSMENU "http://menu.5ch.net/bbsmenu.html"
 
 // スレタイ検索用メニュータイトルアドレス
-#define CONF_MENU_SEARCH_TITLE  "スレタイ検索 (dig.2ch.net)"
-#define CONF_URL_SEARCH_TITLE "http://dig.2ch.net/?maxResult=100&atLeast=1&Link=1&AndOr=0&Sort=5&Bbs=all&924=1&password=dig&keywords=$TEXTU"
+#define CONF_MENU_SEARCH_TITLE  "スレタイ検索 (dig.5ch.net)"
+#define CONF_URL_SEARCH_TITLE "http://dig.5ch.net/?maxResult=100&atLeast=1&Link=1&AndOr=0&Sort=5&Bbs=all&924=1&password=dig&keywords=$TEXTU"
 
 // スレタイ検索用正規表現
-#define CONF_REGEX_SEARCH_TITLE "<a href=\"(http[^\"]*)\">([^<]+) \\(([0-9]{1,4})\\)</a>"
+#define CONF_REGEX_SEARCH_TITLE "<a href=\"(http[^\"]*)\">(.+) \\(([0-9]{1,4})\\)</a></span>"
 
 // WEB検索用メニュータイトルアドレス
 #define CONF_MENU_SEARCH_WEB  "WEB検索 (google)"
 #define CONF_URL_SEARCH_WEB "http://www.google.co.jp/search?hl=ja&q=$TEXTU&btnG=Google+%E6%A4%9C%E7%B4%A2&lr="
 
-// p2 書き込み用アドレス
-
-#define CONF_URL_WRITEP2 "http://p2.2ch.sc/p2/post_form.php?host=$HOST&bbs=$BBSNAME&key=$DATNAME"
-#define CONF_URL_RESP2 "http://p2.2ch.sc/p2/post_form.php?host=$HOST&bbs=$BBSNAME&key=$DATNAME&popup=1&inyou=2&resnum=$NUMBER"
-
 // 2chのクッキー:HAP
 #define CONF_COOKIE_HAP ""
 #define CONF_COOKIE_HAP_BBSPINK ""
 
+// 過去ログを取得する外部サイトのURL
+#define CONF_URL_EXTERNAL_LOG "http://mimizun.com/log/2ch/$BBSNAME/$DATNAME.dat"
+
 // 色
 #define CONF_COLOR_CHAR    "#000000000000"     // スレの文字
 #define CONF_COLOR_CHAR_NAME "#000064640000"   //名前欄の文字色
@@ -291,6 +311,13 @@
 #define CONF_MIGEMO_PATH "/usr/share/migemo/utf-8/migemo-dict"
 #endif
 
+// TLSのルート証明書
+#ifdef DEFAULT_CAFILE
+#define CONF_ROOT_CAFILE DEFAULT_CAFILE
+#else
+#define CONF_ROOT_CAFILE "/etc/ssl/certs/ca-certificates.crt"
+#endif
+
 }
 
 #endif
diff -aurNZ jd-2.8.9-150226/src/config/globalconf.cpp jd-2.8.9-a171004/src/config/globalconf.cpp
--- jd-2.8.9-150226/src/config/globalconf.cpp	2014-03-29 15:36:22.000000000 +0900
+++ jd-2.8.9-a171004/src/config/globalconf.cpp	2016-05-11 02:53:29.470112000 +0900
@@ -19,7 +19,6 @@
 
 CONFIG::ConfigItems* CONFIG::get_confitem()
 {
-    if( ! instance_confitem ) instance_confitem = new CONFIG::ConfigItems();
     return instance_confitem;
 }
 
@@ -36,6 +35,7 @@
 
 const bool CONFIG::load_conf()
 {
+    if( ! instance_confitem ) instance_confitem = new CONFIG::ConfigItems();
     return get_confitem()->load();
 }
 
@@ -94,6 +94,9 @@
 const bool CONFIG::get_use_select_gtkrc(){ return get_confitem()->use_select_gtkrc; }
 void CONFIG::set_use_select_gtkrc( const bool use ){ get_confitem()->use_select_gtkrc = use; }
 
+const bool CONFIG::get_use_color_html(){ return get_confitem()->use_color_html; }
+void CONFIG::set_use_color_html( const bool use ){ get_confitem()->use_color_html = use; }
+
 // ツリービューの行間スペース
 const int CONFIG::get_tree_ypad(){ return get_confitem()->tree_ypad; }
 
@@ -154,8 +157,8 @@
 
 const std::string& CONFIG::get_url_jdhp() { return get_confitem()->url_jdhp; }
 
-// 2chの認証サーバのアドレス
-const std::string& CONFIG::get_url_login2ch() { return get_confitem()->url_login2ch; }
+// 2ch APIサーバのアドレス
+const std::string& CONFIG::get_url_api2ch() { return get_confitem()->url_api2ch; }
 
 // p2の認証サーバのアドレス
 const std::string& CONFIG::get_url_loginp2() { return get_confitem()->url_loginp2; }
@@ -164,6 +167,9 @@
 // BEの認証サーバのアドレス
 const std::string& CONFIG::get_url_loginbe() { return get_confitem()->url_loginbe; }
 
+// 通常のアクセスを行う2chサーバ
+const std::string& CONFIG::get_dedicated_server() { return get_confitem()->dedicated_server; }
+
 const std::string& CONFIG::get_url_bbsmenu() { return get_confitem()->url_bbsmenu; }
 
 const bool CONFIG::use_link_as_board(){ return get_confitem()->use_link_as_board; }
@@ -178,10 +184,8 @@
 const std::string& CONFIG::get_menu_search_web(){ return get_confitem()->menu_search_web; }
 const std::string& CONFIG::get_url_search_web(){ return get_confitem()->url_search_web; }
 
-const std::string& CONFIG::get_url_writep2(){ return get_confitem()->url_writep2; }
-const std::string& CONFIG::get_url_resp2(){ return get_confitem()->url_resp2; }
-
 const std::string& CONFIG::get_agent_for2ch() { return get_confitem()->agent_for2ch; }
+const std::string& CONFIG::get_agent_for_readcgi() { return get_confitem()->agent_for_readcgi; }
 
 const bool CONFIG::get_use_proxy_for2ch() { return get_confitem()->use_proxy_for2ch; }
 const std::string& CONFIG::get_proxy_for2ch() { return get_confitem()->proxy_for2ch; }
@@ -209,6 +213,8 @@
 const std::string& CONFIG::get_proxy_basicauth_for_data() { return get_confitem()->proxy_basicauth_for_data; }
 
 const std::string& CONFIG::get_x_2ch_ua() { return get_confitem()->x_2ch_ua; }
+const std::string& CONFIG::get_appkey() { return get_confitem()->appkey; }
+const std::string& CONFIG::get_hmkey() { return get_confitem()->hmkey; }
 
 void CONFIG::set_use_proxy_for_data( bool set ){ get_confitem()->use_proxy_for_data = set; }
 void CONFIG::set_proxy_for_data( const std::string& proxy ){ get_confitem()->set_proxy_for_data( proxy ); }
@@ -225,6 +231,13 @@
 const bool CONFIG::get_use_ipv6(){ return get_confitem()->use_ipv6; }
 void CONFIG::set_use_ipv6( const bool set ){ get_confitem()->use_ipv6 = set; }
 
+// TLSでノンブロッキングI/Oを使用する
+const bool CONFIG::get_tls_nonblocking(){ return get_confitem()->tls_nonblocking; }
+// TLSでサーバの証明書をチェックする
+const bool CONFIG::get_verify_cert(){ return get_confitem()->verify_cert; }
+// 信頼するルート証明書
+const std::string& CONFIG::get_root_cafile(){ return get_confitem()->root_cafile; }
+
 // 同一ホストに対する最大コネクション数( 1 または 2 )
 const int CONFIG::get_connection_num(){ return get_confitem()->connection_num; }
 
@@ -235,8 +248,12 @@
 void CONFIG::set_cookie_hap( const std::string& cookie_hap ){ get_confitem()->cookie_hap = cookie_hap; }
 void CONFIG::set_cookie_hap_bbspink( const std::string& cookie_hap ){ get_confitem()->cookie_hap_bbspink = cookie_hap; }
 
-// 2chの過去ログ取得時にofflaw2を使用する
-const bool CONFIG::get_use_offlaw2_2ch(){ return get_confitem()->use_offlaw2_2ch; }
+// 2chの過去ログ取得時にread.cgiを使用する
+const bool CONFIG::get_use_2chkako_readcgi(){ return get_confitem()->use_2chkako_readcgi; }
+
+// 2chの過去ログを外部のサイトから取得する
+const bool CONFIG::get_use_external_log(){ return get_confitem()->use_external_log; }
+const std::string& CONFIG::get_url_external_log(){ return get_confitem()->url_external_log; }
 
 const std::string& CONFIG::get_command_openurl() { return get_confitem()->command_openurl; }
 void CONFIG::set_command_openurl( const std::string& command ){ get_confitem()->command_openurl = command; }
@@ -434,6 +451,8 @@
 
 const bool CONFIG::get_loose_url(){ return get_confitem()->loose_url; }
 
+const bool CONFIG::get_percent_decode(){ return get_confitem()->percent_decode; }
+
 const bool CONFIG::get_hide_usrcmd(){ return get_confitem()->hide_usrcmd; }
 void CONFIG::set_hide_usrcmd( const bool hide ){ get_confitem()->hide_usrcmd = hide; }
 
@@ -488,8 +507,11 @@
 const int CONFIG::get_remove_old_abone_thread(){ return get_confitem()->remove_old_abone_thread; } 
 void CONFIG::set_remove_old_abone_thread( const int remove ){ get_confitem()->remove_old_abone_thread = remove; }
 
-const int CONFIG::get_abone_number_thread(){ return get_confitem()->abone_number_thread; } 
-void CONFIG::set_abone_number_thread( const int number ){ get_confitem()->abone_number_thread = number; }
+const int CONFIG::get_abone_min_number_thread(){ return get_confitem()->abone_min_number_thread; }
+void CONFIG::set_abone_min_number_thread( const int number ){ get_confitem()->abone_min_number_thread = number; }
+
+const int CONFIG::get_abone_max_number_thread(){ return get_confitem()->abone_max_number_thread; }
+void CONFIG::set_abone_max_number_thread( const int number ){ get_confitem()->abone_max_number_thread = number; }
 
 const int CONFIG::get_abone_hour_thread(){ return get_confitem()->abone_hour_thread; } 
 void CONFIG::set_abone_hour_thread( const int hour ){ get_confitem()->abone_hour_thread = hour; }
@@ -582,6 +604,13 @@
 // 状態変更時にメインステータスバーの色を変える
 const bool CONFIG::get_change_stastatus_color(){ return get_confitem()->change_stastatus_color; }
 
+// 状態変更時にスレビュータイトルの色を変える
+const bool CONFIG::get_change_statitle_color(){ return get_confitem()->change_statitle_color; }
+
+// 2chのスレ取得に read.cgi を使用する
+const bool CONFIG::get_use_2ch_readcgi(){ return get_confitem()->use_2ch_readcgi; }
+void CONFIG::set_use_2ch_readcgi( const bool set ){ get_confitem()->use_2ch_readcgi = set; }
+
 // まちBBSの取得に offlaw.cgi を使用する
 const bool CONFIG::get_use_machi_offlaw(){ return get_confitem()->use_machi_offlaw; }
 void CONFIG::set_use_machi_offlaw( const bool set ){ get_confitem()->use_machi_offlaw = set; }
@@ -604,3 +633,12 @@
 #ifdef HAVE_MIGEMO_H
 const std::string& CONFIG::get_migemodict_path() { return get_confitem()->migemodict_path; }
 #endif
+
+// 不正なMS932文字列をUTF-8と見なす
+const bool CONFIG::get_broken_sjis_be_utf8(){ return get_confitem()->broken_sjis_be_utf8; }
+void CONFIG::set_broken_sjis_be_utf8( const bool set ){ get_confitem()->broken_sjis_be_utf8 = set; }
+
+// 不正な数値文字参照を無理矢理変換する
+const bool CONFIG::get_correct_character_reference(){ return get_confitem()->correct_character_reference; }
+void CONFIG::set_correct_character_reference( const bool set ){ get_confitem()->correct_character_reference = set; }
+
diff -aurNZ jd-2.8.9-150226/src/config/globalconf.h jd-2.8.9-a171004/src/config/globalconf.h
--- jd-2.8.9-150226/src/config/globalconf.h	2014-03-29 15:36:22.000000000 +0900
+++ jd-2.8.9-a171004/src/config/globalconf.h	2016-05-11 02:54:33.990111000 +0900
@@ -52,6 +52,10 @@
     const bool get_use_select_gtkrc();
     void set_use_select_gtkrc( const bool use );
 
+    // スレビューでHTMLタグ指定の色を使用するか
+    const bool get_use_color_html();
+    void set_use_color_html( const bool use );
+
     // ツリービューの行間スペース
     const int get_tree_ypad();
 
@@ -96,8 +100,8 @@
     // JD ホームページのアドレス
     const std::string& get_url_jdhp();
 
-    // 2chの認証サーバのアドレス
-    const std::string& get_url_login2ch();
+    // 2ch APIサーバのアドレス
+    const std::string& get_url_api2ch();
 
     // p2の認証サーバのアドレス
     const std::string& get_url_loginp2();
@@ -106,6 +110,9 @@
     // BEの認証サーバのアドレス
     const std::string& get_url_loginbe();
 
+    // 通常のアクセスを行う2chサーバ
+    const std::string& get_dedicated_server();
+
     // bbsmenu.htmlのURL
     const std::string& get_url_bbsmenu();    
 
@@ -127,16 +134,21 @@
     const std::string& get_menu_search_web();
     const std::string& get_url_search_web();
 
-    // p2 書き込み用アドレス
-    const std::string& get_url_writep2();
-    const std::string& get_url_resp2();
-
     // 2ch にアクセスするときのエージェント名
     const std::string& get_agent_for2ch();
 
+    // 2chのread.cgiにアクセスするときのエージェント名
+    const std::string& get_agent_for_readcgi();
+
     // 2ch にログインするときのX-2ch-UA
     const std::string& get_x_2ch_ua();
 
+    // 2chの認証に使用するAPPKEY
+    const std::string& get_appkey();
+
+    // 2chの認証に使用するHMKEY
+    const std::string& get_hmkey();
+
     // 2ch 読み込み用プロクシとポート番号
     const bool get_use_proxy_for2ch();
     const std::string& get_proxy_for2ch();
@@ -184,6 +196,15 @@
     const bool get_use_ipv6();
     void set_use_ipv6( const bool set );
 
+    // TLSでノンブロッキングI/Oを使用する
+    const bool get_tls_nonblocking();
+
+    // TLSでサーバの証明書をチェックする
+    const bool get_verify_cert();
+
+    // 信頼するルート証明書
+    const std::string& get_root_cafile();
+
     // 同一ホストに対する最大コネクション数( 1 または 2 )
     const int get_connection_num();
 
@@ -194,8 +215,12 @@
     void set_cookie_hap( const std::string& cookie_hap );
     void set_cookie_hap_bbspink( const std::string& cookie_hap );
 
-    // 2chの過去ログ取得時にofflaw2を使用する
-    const bool get_use_offlaw2_2ch();
+    // 2chの過去ログ取得時にread.cgiを使用する
+    const bool get_use_2chkako_readcgi();
+
+    // 2chの過去ログを外部のサイトから取得する
+    const bool get_use_external_log();
+    const std::string& get_url_external_log();
 
     // リンクをクリックしたときに実行するコマンド
     const std::string& get_command_openurl();
@@ -447,6 +472,9 @@
     // datのパース時にURL判定を甘くする(^なども含める)
     const bool get_loose_url();
 
+    // URLのパーセントコードをデコードして表示する
+    const bool get_percent_decode();
+
     // ユーザーコマンドで選択できない項目を非表示にする
     const bool get_hide_usrcmd();
     void set_hide_usrcmd( const bool hide );
@@ -494,8 +522,11 @@
     const int get_remove_old_abone_thread(); // dat落ちしたスレをNGスレタイトルリストから取り除くか( 0: ダイアログ表示 1: 取り除く 2: 除かない )
     void set_remove_old_abone_thread( const int remove ); 
 
-    const int get_abone_number_thread();
-    void set_abone_number_thread( const int number );
+    const int get_abone_min_number_thread();
+    void set_abone_min_number_thread( const int number );
+
+    const int get_abone_max_number_thread();
+    void set_abone_max_number_thread( const int number );
 
     const int get_abone_hour_thread();
     void set_abone_hour_thread( const int hour );
@@ -570,6 +601,13 @@
     // 状態変更時にメインステータスバーの色を変える
     const bool get_change_stastatus_color();
 
+    // 状態変更時にスレビュータイトルの色を変える
+    const bool get_change_statitle_color();
+
+    // 2chのスレ取得に read.cgi を使用する
+    const bool get_use_2ch_readcgi();
+    void set_use_2ch_readcgi( const bool set );
+
     // まちBBSの取得に offlaw.cgi を使用する
     const bool get_use_machi_offlaw();
     void set_use_machi_offlaw( const bool set );
@@ -593,6 +631,14 @@
     // migemo-dictの場所
     const std::string& get_migemodict_path();
 #endif
+
+    // 不正なMS932文字列をUTF-8と見なす
+    const bool get_broken_sjis_be_utf8();
+    void set_broken_sjis_be_utf8( const bool set );
+
+    // 不正な数値文字参照を無理矢理変換する
+    const bool get_correct_character_reference();
+    void set_correct_character_reference( const bool set );
 }
 
 
diff -aurNZ jd-2.8.9-150226/src/control/Makefile.am jd-2.8.9-a171004/src/control/Makefile.am
--- jd-2.8.9-150226/src/control/Makefile.am	2013-01-06 00:44:26.000000000 +0900
+++ jd-2.8.9-a171004/src/control/Makefile.am	2017-04-11 20:28:53.825712000 +0900
@@ -14,7 +14,7 @@
 	controlutil.h \
 	mousekeyconf.h mousekeyitem.h keyconfig.h mouseconfig.h buttonconfig.h \
 	mousekeypref.h keypref.h mousepref.h buttonpref.h \
-	defaultconf.h
+	defaultconf.h get_config.h
 
 AM_CXXFLAGS = @GTKMM_CFLAGS@
 AM_CPPFLAGS = -I$(top_srcdir)/src
diff -aurNZ jd-2.8.9-150226/src/core.cpp jd-2.8.9-a171004/src/core.cpp
--- jd-2.8.9-150226/src/core.cpp	2015-01-25 15:54:29.000000000 +0900
+++ jd-2.8.9-a171004/src/core.cpp	2017-10-04 15:17:43.030148908 +0900
@@ -14,6 +14,7 @@
 #include "dndmanager.h"
 #include "usrcmdmanager.h"
 #include "linkfiltermanager.h"
+#include "replacestrmanager.h"
 #include "compmanager.h"
 #include "searchmanager.h"
 #include "aamanager.h"
@@ -132,6 +133,9 @@
     // リンクフィルタマネージャ作成
     CORE::get_linkfilter_manager();
 
+    // 文字列置換マネージャ作成
+    CORE::get_replacestr_manager();
+
     // ログ検索マネージャ作成
     CORE::get_search_manager();
 
@@ -177,6 +181,9 @@
     // ログ検索マネージャ削除
     CORE::delete_search_manager();
 
+    // 文字列置換マネージャ削除
+    CORE::delete_replacestr_manager();
+
     // ユーザコマンドマネージャ削除
     CORE::delete_usrcmd_manager();
 
@@ -326,7 +333,7 @@
     m_action_group->add( Gtk::Action::create( "OpenURL", "OpenURL"), sigc::mem_fun( *this, &Core::slot_openurl ) );
     m_action_group->add( Gtk::ToggleAction::create( "Online", "オフライン作業(_W)", std::string(), ! SESSION::is_online() ),
                          sigc::mem_fun( *this, &Core::slot_toggle_online ) );
-    m_action_group->add( Gtk::ToggleAction::create( "Login2ch", "2chにログイン(_L)", std::string(), false ),
+    m_action_group->add( Gtk::ToggleAction::create( "Login2ch", "5chにログイン(_L)", std::string(), false ),
                          sigc::mem_fun( *this, &Core::slot_toggle_login2ch ) );
     m_action_group->add( Gtk::ToggleAction::create( "LoginBe", "BEにログイン(_B)", std::string(), false ),
                         sigc::mem_fun( *this, &Core::slot_toggle_loginbe ) );
@@ -571,11 +578,13 @@
     m_action_group->add( raction_img1, sigc::bind< int >( sigc::mem_fun( *this, &Core::slot_toggle_imgview ), IMGVIEW_EMB ) );
     m_action_group->add( raction_img2, sigc::bind< int >( sigc::mem_fun( *this, &Core::slot_toggle_imgview ), IMGVIEW_NO ) );
 
+    m_action_group->add( Gtk::ToggleAction::create( "UseMosaic", "画像にモザイクをかける(_M)", std::string(), CONFIG::get_use_mosaic() ),
+                         sigc::mem_fun( *this, &Core::slot_toggle_use_mosaic ) );
     m_action_group->add( Gtk::ToggleAction::create( "UseImgPopup", "画像ポップアップを表示する(_P)", std::string(), CONFIG::get_use_image_popup() ),
                          sigc::mem_fun( *this, &Core::slot_toggle_use_imgpopup ) );
     m_action_group->add( Gtk::ToggleAction::create( "UseInlineImg", "インライン画像を表示する(_I)", std::string(), CONFIG::get_use_inline_image() ),
                          sigc::mem_fun( *this, &Core::slot_toggle_use_inlineimg ) );
-    m_action_group->add( Gtk::ToggleAction::create( "ShowSsspIcon", "BEアイコン/エモーションを表示する(_B)", std::string(), CONFIG::get_show_ssspicon() ),
+    m_action_group->add( Gtk::ToggleAction::create( "ShowSsspIcon", "BEアイコン/エモティコンを表示する(_B)", std::string(), CONFIG::get_show_ssspicon() ),
                          sigc::mem_fun( *this, &Core::slot_toggle_show_ssspicon ) );
 
     // リスト表示項目設定
@@ -640,8 +649,8 @@
     m_action_group->add( Gtk::ToggleAction::create( "SavePostHist", "書き込み履歴(鉛筆マーク)を保存する(_P)", std::string(), CONFIG::get_save_post_history() ),
                          sigc::mem_fun( *this, &Core::slot_toggle_save_post_history ) );
 
-    m_action_group->add( Gtk::ToggleAction::create( "UseMosaic", "画像にモザイクをかける(_M)", std::string(), CONFIG::get_use_mosaic() ),
-                         sigc::mem_fun( *this, &Core::slot_toggle_use_mosaic ) );
+    m_action_group->add( Gtk::ToggleAction::create( "Use2chReadcgi", "5chのスレ取得でread.cgiを使用する(_H)", std::string(), CONFIG::get_use_2ch_readcgi() ),
+                         sigc::mem_fun( *this, &Core::slot_toggle_use_2ch_readcgi ) );
 
     m_action_group->add( Gtk::ToggleAction::create( "UseMachiOfflaw", "まちBBSでofflaw.cgiを使用する(_O)", std::string(), CONFIG::get_use_machi_offlaw() ),
                          sigc::mem_fun( *this, &Core::slot_toggle_use_machi_offlaw ) );
@@ -709,6 +718,7 @@
     m_action_group->add( Gtk::Action::create( "LivePref", "実況設定(_L)..." ), sigc::mem_fun( *this, &Core::slot_setup_live ) );
     m_action_group->add( Gtk::Action::create( "UsrCmdPref", "ユーザコマンドの編集(_U)..." ), sigc::mem_fun( *this, &Core::slot_usrcmd_pref ) );
     m_action_group->add( Gtk::Action::create( "FilterPref", "リンクフィルタの編集(_F)..." ), sigc::mem_fun( *this, &Core::slot_filter_pref ) );
+    m_action_group->add( Gtk::Action::create( "ReplacePref", "置換文字列の編集(_R)..." ), sigc::mem_fun( *this, &Core::slot_replace_pref ) );
     m_action_group->add( Gtk::Action::create( "AboutConfig", "about:config 高度な設定(_C)..." ), sigc::mem_fun( *this, &Core::slot_aboutconfig ) );
 
 
@@ -924,6 +934,7 @@
                         "<menuitem action='UseEmbImg'/>"
                         "<menuitem action='NoUseImg'/>"
                     "</menu>"
+                    "<menuitem action='UseMosaic'/>"
                     "<menuitem action='UseImgPopup'/>"
                     "<menuitem action='UseInlineImg'/>"
                     "<menuitem action='ShowSsspIcon'/>"
@@ -1005,8 +1016,7 @@
                 "<menuitem action='SavePostLog'/>"
                 "<menuitem action='SavePostHist'/>"
                 "<separator/>"
-                "<menuitem action='UseMosaic'/>"
-                "<separator/>"
+                "<menuitem action='Use2chReadcgi'/>"
                 "<menuitem action='UseMachiOfflaw'/>"
             "</menu>"
             "<separator/>"
@@ -1062,6 +1072,7 @@
                 "<menuitem action='LivePref'/>"
                 "<menuitem action='UsrCmdPref'/>"
                 "<menuitem action='FilterPref'/>"
+                "<menuitem action='ReplacePref'/>"
             "</menu>"
             "<separator/>"
             "<menuitem action='AboutConfig'/>"
@@ -1368,10 +1379,14 @@
     if( m_title.empty() ) title = "JD - " + ENVIRONMENT::get_jdversion();
     else title = "JD - " + m_title;
 
-    if( CORE::get_login2ch()->login_now() ) title +=" [ ● ]";
+    if( CORE::get_login2ch()->login_now() ) {
+        if( CORE::get_login2ch()->get_username().empty() ) title +=" [ Anonymous ]";
+        else title +=" [ Ronin ]";
+    }
+
     if( CORE::get_loginbe()->login_now() ) title +=" [ BE ]";
     if( CORE::get_loginp2()->login_now() ) title +=" [ p2 ]";
-    if( ! SESSION::is_online() ) title += " [ オフライン ]";
+    if( ! SESSION::is_online() ) title += " [ offline ]";
     m_win_main.set_title( title );
 }
 
@@ -1536,17 +1551,17 @@
 
     // 開いている板のログ検索
     act = m_action_group->get_action( "SearchCacheBoard" );
-    if( BOARD::get_admin()->empty() || DBTREE::url_subject( BOARD::get_admin()->get_current_url() ).empty() ) act->set_sensitive( false );
+    if( BOARD::get_admin()->empty() || DBTREE::url_boardbase( BOARD::get_admin()->get_current_url() ).empty() ) act->set_sensitive( false );
     else act->set_sensitive( true );
 
     // 開いている板のログ一覧表示
     act = m_action_group->get_action( "ShowCacheBoard" );
-    if( BOARD::get_admin()->empty() || DBTREE::url_subject( BOARD::get_admin()->get_current_url() ).empty() ) act->set_sensitive( false );
+    if( BOARD::get_admin()->empty() || DBTREE::url_boardbase( BOARD::get_admin()->get_current_url() ).empty() ) act->set_sensitive( false );
     else act->set_sensitive( true );
 
     // スレ一覧のプロパティ
     act = m_action_group->get_action( "BoardPref" );
-    if( BOARD::get_admin()->empty() || DBTREE::url_subject( BOARD::get_admin()->get_current_url() ).empty() ) act->set_sensitive( false );
+    if( BOARD::get_admin()->empty() || DBTREE::url_boardbase( BOARD::get_admin()->get_current_url() ).empty() ) act->set_sensitive( false );
     else act->set_sensitive( true );
     
     // スレのプロパティ
@@ -2278,10 +2293,6 @@
             }
         }
 
-        DBTREE::delete_article( command.url, ( command.arg1 == "reget" ) );
-
-        if( DBTREE::article_is_cached( command.url ) ) return;
-
         ARTICLE::get_admin()->set_command( "unlock_views", command.url );
         ARTICLE::get_admin()->set_command( "close_view", command.url,
                                            "closeall" // command.url を含む全てのビューを閉じる
@@ -2292,6 +2303,10 @@
                                          "closeall" // command.url を含む全てのビューを閉じる
             );
 
+        DBTREE::delete_article( command.url, ( command.arg1 == "reget" ) );
+
+        if( DBTREE::article_is_cached( command.url ) ) return;
+
         // ポップアップも削除して対象となるarticlebaseのロックを解除 (注) ポップアップは遅延してdeleteされる
         // そうしないと articlebase::unlock_impl()が呼び出されないためnotetreebaseが削除されない
         ARTICLE::get_admin()->set_command( "delete_all_popups" );  
@@ -2318,7 +2333,7 @@
 
     // 全articleviewの再レイアウト
     else if( command.command == "relayout_all_article" ){
-        ARTICLE::get_admin()->set_command( "relayout_all" );
+        ARTICLE::get_admin()->set_command( "relayout_all", command.url, command.arg1 );
     }
 
     // 全articleviewのフォントの初期化
@@ -3242,7 +3257,7 @@
         int num_from, num_to;
         std::string num_str;
         const std::string url_dat = DBTREE::url_dat( command.url, num_from, num_to, num_str );
-        const std::string url_subject = DBTREE::url_subject( command.url );
+        const std::string boardbase = DBTREE::url_boardbase( command.url );
        
         // datの場合ビューで開く
         if( ! url_dat.empty() ){
@@ -3282,13 +3297,13 @@
         }
 
         // 掲示板のベースURLの場合
-        else if( ! url_subject.empty() ){
+        else if( ! boardbase.empty() ){
 
 #ifdef _DEBUG
-            std::cout << "exec : open_board url = " << url_subject << std::endl;
+            std::cout << "exec : open_board url = " << boardbase << std::endl;
 #endif
 
-            CORE::core_set_command( "open_board" , url_subject, "true" );
+            CORE::core_set_command( "open_board" , boardbase, "true" );
         }
 
         // その他
@@ -4267,10 +4282,10 @@
 {
     if( ! list_files.size() ) return;
 
-    const std::string url_subject = DBTREE::url_subject( url_board );
+    const std::string boardbase = DBTREE::url_boardbase( url_board );
 
 #ifdef _DEBUG
-    std::cout << "Core::import_dat url = " << url_subject << std::endl;
+    std::cout << "Core::import_dat url = " << boardbase << std::endl;
 #endif
 
     CORE::DATA_INFO_LIST list_info;
@@ -4286,7 +4301,7 @@
         std::cout << filename << std::endl;
 #endif
 
-        std::string url = DBTREE::board_import_dat( url_subject, filename );
+        std::string url = DBTREE::board_import_dat( boardbase, filename );
         if( ! url.empty() ){
             info.url = url;
             list_info.push_back( info );
@@ -4295,10 +4310,10 @@
 
     if( list_info.size() ){
 
-        CORE::core_set_command( "open_board" , url_subject, "true" , "auto" );
+        CORE::core_set_command( "open_board" , boardbase, "true" , "auto" );
 
         CORE::SBUF_set_list( list_info );
-        BOARD::get_admin()->set_command( "draw_bg_articles", url_subject );
+        BOARD::get_admin()->set_command( "draw_bg_articles", boardbase );
     }
 }
 
diff -aurNZ jd-2.8.9-150226/src/core.h jd-2.8.9-a171004/src/core.h
--- jd-2.8.9-150226/src/core.h	2014-03-29 15:36:22.000000000 +0900
+++ jd-2.8.9-a171004/src/core.h	2016-02-01 00:24:08.098986000 +0900
@@ -279,6 +279,7 @@
         void slot_toggle_save_post_log();
         void slot_toggle_save_post_history();
         void slot_toggle_use_mosaic();
+        void slot_toggle_use_2ch_readcgi();
         void slot_toggle_use_machi_offlaw();
         void slot_toggle_tabbutton();
         void slot_toggle_popupwarpmode();
@@ -306,6 +307,7 @@
         void slot_setup_live();
         void slot_usrcmd_pref();
         void slot_filter_pref();
+        void slot_replace_pref();
         void slot_aboutconfig();
         void slot_clear_privacy();
         void slot_clear_post_log();
diff -aurNZ jd-2.8.9-150226/src/cssmanager.cpp jd-2.8.9-a171004/src/cssmanager.cpp
--- jd-2.8.9-150226/src/cssmanager.cpp	2014-07-13 21:09:49.000000000 +0900
+++ jd-2.8.9-a171004/src/cssmanager.cpp	2017-03-26 02:28:08.659626000 +0900
@@ -85,7 +85,7 @@
 //
 // ユーザ設定の色取得
 // 
-std::string Css_Manager::get_color( int colorid )
+std::string Css_Manager::get_color( int colorid ) const
 {
     colorid -= USRCOLOR_BASE;
     return m_colors[ colorid ];
@@ -95,11 +95,11 @@
 //
 // クラス名からID取得
 //
-int Css_Manager::get_classid( const std::string& classname )
+int Css_Manager::get_classid( const std::string& classname ) const
 {
     int id = 0;
 
-    std::list< std::string >::iterator it = m_css_class.begin();
+    std::list< std::string >::const_iterator it = m_css_class.begin();
     for( ; it != m_css_class.end(); ++it, ++id ) if( ( *it ) == classname ) return id;
 
     return -1;
@@ -202,6 +202,15 @@
     css.border_bottom_width_px = 1;
 
     set_property( "imgpopup", css );
+
+
+    /////////////////
+    // mark
+    std::map< std::string, std::string > pair;
+    pair.insert( make_pair( std::string( "color"), std::string( "black" ) ) );
+    pair.insert( make_pair( std::string( "background-color" ), std::string( "yellow" ) ) );
+
+    set_property( "mark", create_property( pair ) );
 }
 
 
@@ -546,7 +555,7 @@
 //
 // 文字の高さを与えてemをセット
 //
-void Css_Manager::set_size( CSS_PROPERTY* css, double height )
+void Css_Manager::set_size( CSS_PROPERTY* css, double height ) const
 {
     if( ! css ) return;
     if( ! height ) return;
@@ -665,7 +674,7 @@
 #endif
 
     DOM* tmpdom = create_domnode( DOMNODE_TEXT );
-    tmpdom->chardat = ( char* ) m_heap.heap_alloc( lng );
+    tmpdom->chardat = ( char* ) m_heap.heap_alloc( lng + 1 );
     strncpy( tmpdom->chardat, text, lng );
 
     return tmpdom;
diff -aurNZ jd-2.8.9-150226/src/cssmanager.h jd-2.8.9-a171004/src/cssmanager.h
--- jd-2.8.9-150226/src/cssmanager.h	2014-07-13 21:09:49.000000000 +0900
+++ jd-2.8.9-a171004/src/cssmanager.h	2017-03-26 02:27:40.155627000 +0900
@@ -150,19 +150,19 @@
         virtual ~Css_Manager(){}
 
         // ユーザ設定の色取得
-        std::string get_color( int colorid );
+        std::string get_color( int colorid ) const;
 
         // ユーザ設定の色( 先頭は黒 )
         std::vector< std::string >& get_colors() { return m_colors; }
 
         // クラス名からID取得
-        int get_classid( const std::string& classname );
+        int get_classid( const std::string& classname ) const;
 
         // プロパティ取得
         CSS_PROPERTY get_property( const int id );
 
         // 文字の高さを与えてemをセット
-        void set_size( CSS_PROPERTY* css, double height );
+        void set_size( CSS_PROPERTY* css, double height ) const;
 
         // DOM 取得
         const DOM* get_dom() const { return m_dom; }
diff -aurNZ jd-2.8.9-150226/src/dbtree/article2ch.cpp jd-2.8.9-a171004/src/dbtree/article2ch.cpp
--- jd-2.8.9-150226/src/dbtree/article2ch.cpp	2014-04-13 05:08:36.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/article2ch.cpp	2016-06-28 20:16:57.349309161 +0900
@@ -7,6 +7,7 @@
 #include "nodetree2ch.h"
 #include "interface.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/misctime.h"
 
@@ -20,8 +21,8 @@
 using namespace DBTREE;
 
 
-Article2ch::Article2ch( const std::string& datbase, const std::string& id, bool cached )
-    : Article2chCompati( datbase, id, cached )
+Article2ch::Article2ch( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode )
+    : Article2chCompati( datbase, id, cached, charcode )
 {}
 
 
@@ -35,41 +36,39 @@
 {
     if( msg.empty() ) return std::string();
 
-    const std::string charset = DBTREE::board_charset( get_url() );
-
     std::stringstream ss_post;
     ss_post.clear();
     ss_post << "bbs="      << DBTREE::board_id( get_url() )
             << "&key="     << get_key();
 
     // キーワード( hana=mogera や suka=pontan など )
-    const std::string keyword = DBTREE::board_keyword_for_write( get_url() );
+    const std::string& keyword = DBTREE::board_keyword_for_write( get_url() );
     if( ! keyword.empty() ) ss_post << "&" << keyword;
 
     // ログイン中
-    if( CORE::get_login2ch()->login_now() ){
-                std::string sid = CORE::get_login2ch()->get_sessionid();
-                ss_post << "&sid=" << MISC::url_encode( sid.c_str(), sid.length() );
+    if( CORE::get_login2ch()->login_now() && ! CORE::get_login2ch()->get_username().empty() ){
+        std::string sid = CORE::get_login2ch()->get_sessionid();
+        ss_post << "&sid=" << MISC::url_encode( sid );
     }
 
     ss_post << "&time="    << get_time_modified()
-            << "&submit="  << MISC::charset_url_encode( "書き込む", charset )
-            << "&FROM="    << MISC::charset_url_encode( name, charset )
-            << "&mail="    << MISC::charset_url_encode( mail, charset )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, charset );
+            << "&submit="  << MISC::url_encode( std::string( "書き込む" ), get_charcode() )
+            << "&FROM="    << MISC::url_encode( name, get_charcode() )
+            << "&mail="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() );
 
     if( CORE::get_loginp2()->login_now() ){
 
-        ss_post << "&detect_hint=" << MISC::charset_url_encode( "◎◇", charset )
+        ss_post << "&detect_hint=" << MISC::url_encode( std::string( "◎◇" ), get_charcode() )
                 << "&host=" << MISC::url_encode( MISC::get_hostname( get_url(), false ) )
                 << "&popup=1"
                 << "&rescount=" << get_number_load()
-                << "&ttitle_en=" << MISC::url_encode( MISC::base64( MISC::Iconv( MISC::remove_space( get_subject() ), "UTF-8", charset ) ) )
+                << "&ttitle_en=" << MISC::url_encode( MISC::base64( MISC::Iconv( get_subject(), CHARCODE_UTF8, get_charcode() ) ) )
                 << "&csrfid=" << MISC::url_encode( CORE::get_loginp2()->get_sessiondata() );
     }
 
 #ifdef _DEBUG
-    std::cout << "Article2chCompati::create_write_message " << ss_post.str() << std::endl;
+    std::cout << "Article2ch::create_write_message " << ss_post.str() << std::endl;
 #endif
 
     return ss_post.str();
@@ -118,7 +117,16 @@
 const bool Article2ch::is_load_olddat()
 {
     // 2chにログインしている場合
-    // または、offlaw2を使う設定の場合 ( bbspinkを除く )
-    return CORE::get_login2ch()->login_now()
-            || ( CONFIG::get_use_offlaw2_2ch() && get_url().find( ".bbspink.com" ) == std::string::npos );
+    // または、read.cgiを使う設定の場合
+    return ( CORE::get_login2ch()->login_now() && ! CORE::get_login2ch()->get_username().empty() )
+            || CONFIG::get_use_2ch_readcgi() || CONFIG::get_use_2chkako_readcgi()
+            || CONFIG::get_use_external_log();
+}
+
+
+// read.cgiを使わない時は更新チェック可能
+const bool Article2ch::enable_check_update()
+{
+    return DBTREE::board_is_dedicated_server( get_url() )
+        || ( ! CORE::get_login2ch()->login_now() && ! CONFIG::get_use_2ch_readcgi() );
 }
diff -aurNZ jd-2.8.9-150226/src/dbtree/article2ch.h jd-2.8.9-a171004/src/dbtree/article2ch.h
--- jd-2.8.9-150226/src/dbtree/article2ch.h	2014-04-13 05:08:36.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/article2ch.h	2015-12-31 11:35:32.091321000 +0900
@@ -15,7 +15,7 @@
     {
       public:
 
-        Article2ch( const std::string& datbase, const std::string& id, bool cached );
+        Article2ch( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode );
         ~Article2ch();
 
         // 書き込みメッセージ変換
@@ -34,6 +34,8 @@
 
       private:
         
+        virtual const bool enable_check_update();
+
         virtual NodeTreeBase* create_nodetree();
     };
 }
diff -aurNZ jd-2.8.9-150226/src/dbtree/article2chcompati.cpp jd-2.8.9-a171004/src/dbtree/article2chcompati.cpp
--- jd-2.8.9-150226/src/dbtree/article2chcompati.cpp	2009-12-23 22:30:11.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/article2chcompati.cpp	2015-12-31 12:40:22.759262000 +0900
@@ -16,8 +16,8 @@
 
 using namespace DBTREE;
 
-Article2chCompati::Article2chCompati( const std::string& datbase, const std::string& _id, bool cached )
-    : ArticleBase( datbase, _id, cached )
+Article2chCompati::Article2chCompati( const std::string& datbase, const std::string& _id, bool cached, const CharCode charcode )
+    : ArticleBase( datbase, _id, cached, charcode )
 {
     assert( ! get_id().empty() );
 
@@ -45,17 +45,15 @@
 {
     if( msg.empty() ) return std::string();
 
-    std::string charset = DBTREE::board_charset( get_url() );
-
     std::stringstream ss_post;
     ss_post.clear();
     ss_post << "bbs="      << DBTREE::board_id( get_url() )
             << "&key="     << get_key()
             << "&time="    << get_time_modified()
-            << "&submit="  << MISC::charset_url_encode( "書き込む", charset )
-            << "&FROM="    << MISC::charset_url_encode( name, charset )
-            << "&mail="    << MISC::charset_url_encode( mail, charset )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, charset );
+            << "&submit="  << MISC::url_encode( std::string( "書き込む" ), get_charcode() )
+            << "&FROM="    << MISC::url_encode( name, get_charcode() )
+            << "&mail="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() );
 
 #ifdef _DEBUG
     std::cout << "Article2chCompati::create_write_message " << ss_post.str() << std::endl;
diff -aurNZ jd-2.8.9-150226/src/dbtree/article2chcompati.h jd-2.8.9-a171004/src/dbtree/article2chcompati.h
--- jd-2.8.9-150226/src/dbtree/article2chcompati.h	2009-05-16 20:42:26.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/article2chcompati.h	2015-12-31 11:35:32.147321000 +0900
@@ -15,7 +15,7 @@
     {
       public:
 
-        Article2chCompati( const std::string& datbase, const std::string& id, bool cached );
+        Article2chCompati( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode  );
         virtual ~Article2chCompati();
 
         // 書き込みメッセージ変換
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlebase.cpp jd-2.8.9-a171004/src/dbtree/articlebase.cpp
--- jd-2.8.9-150226/src/dbtree/articlebase.cpp	2014-04-13 05:08:36.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlebase.cpp	2017-03-30 17:38:22.445491000 +0900
@@ -9,6 +9,7 @@
 
 #include "skeleton/msgdiag.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/misctime.h"
 #include "jdlib/miscmsg.h"
@@ -24,6 +25,7 @@
 #include "cache.h"
 #include "global.h"
 #include "login2ch.h"
+#include "replacestrmanager.h"
 #include "session.h"
 #include "updatemanager.h"
 
@@ -59,7 +61,7 @@
 
 
 
-ArticleBase::ArticleBase( const std::string& datbase, const std::string& id, bool cached )
+ArticleBase::ArticleBase( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode )
     : SKELETON::Lockable(),
       m_id ( id ),
       m_key( std::string() ),
@@ -68,6 +70,7 @@
       m_code( HTTP_INIT ),
       m_str_code( std::string() ),
       m_status( STATUS_UNKNOWN ),
+      m_charcode( charcode ),
       m_subject( std::string() ),
       m_number( 0 ),
       m_number_diff( 0 ),
@@ -81,6 +84,8 @@
       m_abone_transparent( false ),
       m_abone_chain( false ),
       m_abone_age( false ),
+      m_abone_default_name( false ),
+      m_abone_noid( false ),
       m_abone_board( true ),
       m_abone_global( true ),
       m_bookmarked_thread( false ),
@@ -341,22 +346,13 @@
 
 
 //
-// number 番のレスの生文字列を返す
-//
-const std::string ArticleBase::get_raw_res_str( int number )
-{
-    return get_nodetree()->get_raw_res_str( number );
-}
-
-
-//
 // 更新時刻
 //
 time_t ArticleBase::get_time_modified()
 {
     time_t time_out;
     time_out = MISC::datetotime( m_date_modified );
-    if( time_out == 0 ) time_out = time( NULL ) - 600;
+    if( time_out == 0 ) time_out = time( NULL ) - ( 60 * 60 );
     return time_out; 
 }
 
@@ -458,25 +454,37 @@
 }
 
 
-void ArticleBase::set_subject( const std::string& subject )
+const std::string& ArticleBase::get_modified_subject( const bool renew )
 {
-    if( subject.empty() ) return;
+    if( renew || m_modified_subject.empty() ){
+        CORE::ReplaceStr_Manager *mgr = CORE::get_replacestr_manager();
 
-    // 特殊文字の置き換え
-    if( subject.find( "&" ) != std::string::npos ){
-
-        std::string subject_tmp = MISC::html_unescape( subject );
-
-        if( subject_tmp != m_subject ){
-            m_subject = subject_tmp;
-            m_save_info = true;
+        // タイトル文字列置換
+        if( mgr->list_size( CORE::REPLACETARGET_SUBJECT ) ){
+            m_modified_subject = mgr->replace( m_subject.c_str(), m_subject.length(), CORE::REPLACETARGET_SUBJECT );
         }
+        else m_modified_subject = m_subject;
+
     }
-    else if( subject != m_subject ){
 
-        m_subject = subject;
+    return m_modified_subject;
+}
+
+
+void ArticleBase::set_subject( const std::string& subject )
+{
+    if( subject.empty() ) return;
+
+    std::string tmp_subject = MISC::remove_spaces( subject );
+
+    if( tmp_subject != m_subject ){
+        m_subject = tmp_subject;
         m_save_info = true;
     }
+
+    // 置換設定に変更がある場合も読み直すのでm_subjectが同じでも
+    // 次に読み込まれる時に生成させる為にクリア
+    m_modified_subject.clear();
 }
 
 
@@ -500,7 +508,7 @@
     // subject.txt に示されたレス数よりも実際の取得数の方が多い
     else if( is_online && number < m_number ){
 #ifdef _DEBUG
-        std::cout << "ArticleBase::set_number : broken_subject " << get_subject() << " "
+        std::cout << "ArticleBase::set_number : broken_subject " << m_subject << " "
                   << number << " / " << m_number << std::endl;
 #endif
         m_status |= STATUS_BROKEN_SUBJECT;
@@ -523,6 +531,15 @@
 }
 
 
+void ArticleBase::set_number_max( const int number_max )
+{
+    if( number_max != m_number_max ){
+        m_number_max = number_max;
+        m_save_info = true;
+    }
+}
+
+
 // 最終書き込み時間( string型 )
 const std::string& ArticleBase::get_write_date()
 {
@@ -557,7 +574,7 @@
         m_save_info = true;
 
         // BoardViewの行を更新
-        CORE::core_set_command( "update_board_item", DBTREE::url_subject( m_url ), m_id );
+        CORE::core_set_command( "update_board_item", DBTREE::url_boardbase( m_url ), m_id );
     }
 
     // 板の書き込み時間を更新
@@ -587,7 +604,7 @@
     save_info( true );
 
     // BoardViewの行を更新
-    CORE::core_set_command( "update_board_item", DBTREE::url_subject( m_url ), m_id );
+    CORE::core_set_command( "update_board_item", DBTREE::url_boardbase( m_url ), m_id );
 }
 
 
@@ -605,13 +622,17 @@
 //
 const bool ArticleBase::is_finished()
 {
-    if( is_cached() && ! enable_load() &&  m_number_max && get_number_seen() >= m_number_max ){
+    if( is_cached() && ! enable_load() ){
+        int number_max = ( m_number_max != 0 ) ? m_number_max :
+                                DBTREE::board_get_number_max_res( m_url );
+        if( number_max && m_number_load >= number_max ){
 
 #ifdef _DEBUG
-        std::cout << "ArticleBase::is_finished :  seen = " << get_number_seen() << " max = " << m_number_max << " : " << get_subject() << std::endl;
+            std::cout << "ArticleBase::is_finished :  load = " << m_number_load << " max = " << number_max << " : " << m_subject << std::endl;
 #endif
 
-        return true;
+            return true;
+        }
     }
 
     return false;
@@ -663,7 +684,8 @@
     if( ! m_nodetree ) return;
 
     get_nodetree()->copy_abone_info( m_list_abone_id, m_list_abone_name, m_list_abone_word, m_list_abone_regex, m_vec_abone_res,
-                                     m_abone_transparent, m_abone_chain, m_abone_age, m_abone_board, m_abone_global );
+                                     m_abone_transparent, m_abone_chain, m_abone_age, m_abone_default_name, m_abone_noid,
+                                     m_abone_board, m_abone_global );
 
     get_nodetree()->update_abone_all();
 }
@@ -679,6 +701,7 @@
                                const std::list< std::string >& regexs,
                                const std::vector< char >& vec_abone_res,
                                const bool transparent, const bool chain, const bool age,
+                               const bool default_name, const bool noid,
                                const bool board, const bool global
     )
 {
@@ -715,6 +738,8 @@
     m_abone_transparent = transparent;
     m_abone_chain = chain;
     m_abone_age = age;
+    m_abone_default_name = default_name;
+    m_abone_noid = noid;
     m_abone_board = board;
     m_abone_global = global;
 
@@ -858,6 +883,36 @@
 
 
 //
+// デフォルト名無しあぼーん更新
+//
+void ArticleBase::set_abone_default_name( const bool set )
+{
+    if( empty() ) return;
+
+    m_abone_default_name = set;
+
+    update_abone();
+
+    m_save_info = true;
+}
+
+
+//
+// ageあぼーん更新
+//
+void ArticleBase::set_abone_noid( const bool set )
+{
+    if( empty() ) return;
+
+    m_abone_noid = set;
+
+    update_abone();
+
+    m_save_info = true;
+}
+
+
+//
 // 板レベルでのあぼーんを有効にする
 //
 void ArticleBase::set_abone_board( const bool set )
@@ -1025,7 +1080,8 @@
 
         // あぼーん情報のコピー
         m_nodetree->copy_abone_info( m_list_abone_id, m_list_abone_name, m_list_abone_word, m_list_abone_regex, m_vec_abone_res,
-                                     m_abone_transparent, m_abone_chain, m_abone_age, m_abone_board, m_abone_global );
+                                     m_abone_transparent, m_abone_chain, m_abone_age, m_abone_default_name, m_abone_noid,
+                                     m_abone_board, m_abone_global );
 
         // 書き込み情報のコピー
         m_nodetree->copy_post_info( m_vec_posted );
@@ -1071,7 +1127,7 @@
 //
 // ロード中か
 //
-const bool ArticleBase::is_loading()
+const bool ArticleBase::is_loading() const
 {
     if( ! m_nodetree ) return false;
     return m_nodetree->is_loading();
@@ -1081,7 +1137,7 @@
 //
 // 更新チェック中か
 //
-const bool ArticleBase::is_checking_update()
+const bool ArticleBase::is_checking_update() const
 {
     if( ! is_loading() ) return false;
 
@@ -1179,6 +1235,9 @@
         return;
     }
 
+    // 全レス既読ならロードしない
+    if( is_finished() ) return;
+
 #ifdef _DEBUG
     std::cout << "start\n";
 #endif       
@@ -1250,10 +1309,12 @@
     const bool transparent = DBTREE::get_abone_transparent( url_src );
     const bool chain = DBTREE::get_abone_chain( url_src );
     const bool age = DBTREE::get_abone_age( url_src );
+    const bool default_name = DBTREE::get_abone_default_name( url_src );
+    const bool noid = DBTREE::get_abone_noid( url_src );
     const bool board = DBTREE::get_abone_board( url_src );
     const bool global = DBTREE::get_abone_global( url_src );
 
-    reset_abone( ids, names ,words, regexs, vec_abone_res, transparent, chain, age, board, global );
+    reset_abone( ids, names ,words, regexs, vec_abone_res, transparent, chain, age, default_name, noid, board, global );
 }
 
 
@@ -1280,6 +1341,13 @@
         // スレの読み込み数更新
         m_number_load = m_nodetree->get_res_number();
 
+        // スレの最大レス数更新
+        if( m_number_max == 0 ){
+            int res_max = m_nodetree->get_res_number_max();
+            if( res_max > 0 ) m_number_max = res_max;
+            else if( res_max == 0 ) m_number_max = MAX_RESNUMBER;
+        }
+
         // 対応するarticleビューを更新
         CORE::core_set_command( "update_article", m_url );
     }
@@ -1346,7 +1414,7 @@
             DBTREE::board_show_updateicon( m_url, true );
 
             // スレ一覧の ! 行のアイコンを更新マークにする
-            CORE::core_set_command( "update_board_item", DBTREE::url_subject( m_url ), m_id );
+            CORE::core_set_command( "update_board_item", DBTREE::url_boardbase( m_url ), m_id );
         }
 
         // code と modified を戻しておく
@@ -1523,7 +1591,7 @@
 #endif
 
     // 対応するBoardビューの行を更新
-    CORE::core_set_command( "update_board_item", DBTREE::url_subject( m_url ), m_id );
+    CORE::core_set_command( "update_board_item", DBTREE::url_boardbase( m_url ), m_id );
     
     // articleビューに終了を知らせる
     CORE::core_set_command( "update_article", m_url );
@@ -1612,7 +1680,7 @@
 
         if( m_bookmarked_thread ){
 
-            const std::string msg = "「" + get_subject() +
+            const std::string msg = "「" + MISC::to_plain( get_modified_subject() ) +
             "」にはしおりが付けられています。\n\nスレを削除しますか?\n\nしおりを解除するにはスレの上で右クリックしてしおり解除を選択してください。";
 
             SKELETON::MsgDiag mdiag( NULL, msg, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO );
@@ -1622,7 +1690,7 @@
 
         if( CONFIG::get_show_del_written_thread_diag() && m_write_time.tv_sec ){
 
-            const std::string msg = "「" + get_subject() + "」には書き込み履歴が残っています。\n\nスレを削除しますか?";
+            const std::string msg = "「" + MISC::to_plain( get_modified_subject() ) + "」には書き込み履歴が残っています。\n\nスレを削除しますか?";
 
             SKELETON::MsgCheckDiag mdiag( NULL, msg,
                                           "今後表示しない(常に削除)(_D)",
@@ -1638,11 +1706,11 @@
 
             bool delete_img_cache = false;
 
-            std::list< std::string > list_urls = get_nodetree()->get_urls();
-            std::list< std::string >::iterator it = list_urls.begin();
+            const std::list< std::string > list_urls = get_nodetree()->get_imglinks();
+            std::list< std::string >::const_iterator it = list_urls.begin();
             for( ; it != list_urls.end(); ++it ){
 
-                if( DBIMG::get_type_ext( *it ) != DBIMG::T_UNKNOWN && DBIMG::is_cached( *it ) ){
+                if( DBIMG::is_cached( *it ) ){
                     delete_img_cache = true;
                     break;
                 }
@@ -1652,7 +1720,7 @@
 
                 if( CONFIG::get_delete_img_in_thread() == 0 ){
 
-                    const std::string msg = "「" + get_subject() + "」には画像が貼られています。\n\n画像のキャッシュも削除しますか?";
+                    const std::string msg = "「" + MISC::to_plain( get_modified_subject() ) + "」には画像が貼られています。\n\n画像のキャッシュも削除しますか?";
 
                     SKELETON::MsgCheckDiag mdiag( NULL, msg,
                                                   "今後表示しない(常に削除しない)(_D)",
@@ -1677,7 +1745,7 @@
                     it = list_urls.begin();
                     for( ; it != list_urls.end(); ++it ){
 
-                        if( DBIMG::get_type_ext( *it ) != DBIMG::T_UNKNOWN && DBIMG::is_cached( *it ) ){
+                        if( DBIMG::is_cached( *it ) ){
 
 #ifdef _DEBUG
                             std::cout << "delete " << *it << std::endl;
@@ -1721,6 +1789,8 @@
         m_abone_transparent = false;
         m_abone_chain = false;
         m_abone_age = false;
+        m_abone_default_name = false;
+        m_abone_noid = false;
         m_abone_board = true;
         m_abone_global = true;
         m_read_info = false;
@@ -1742,7 +1812,7 @@
     if( CACHE::file_exists( path_dat ) == CACHE::EXIST_FILE ) unlink( to_locale_cstr( path_dat ) );
 
     // BoardViewの行を更新
-    CORE::core_set_command( "update_board_item", DBTREE::url_subject( m_url ), m_id );
+    CORE::core_set_command( "update_board_item", DBTREE::url_boardbase( m_url ), m_id );
 
     // サイドバーのアイコン表示を戻す
     CORE::core_set_command( "toggle_sidebar_articleicon", m_url );
@@ -1814,6 +1884,7 @@
 
         // subject
         GET_INFOVALUE( m_subject, "subject = " );
+        m_modified_subject.clear(); // 読み込まれる時に生成
 
         // 旧ホスト名
         GET_INFOVALUE( m_org_host, "org_host = " );
@@ -1830,6 +1901,11 @@
         GET_INFOVALUE( str_tmp, "seen = " );
         if( ! str_tmp.empty() ) m_number_seen = atoi( str_tmp.c_str() );
 
+        // 最大レス数
+        m_number_max = 0;
+        GET_INFOVALUE( str_tmp, "max = " );
+        if( ! str_tmp.empty() ) m_number_max = atoi( str_tmp.c_str() );
+
         // 更新時間 (time)
         GET_INFOVALUE( m_date_modified, "modified = " );
 
@@ -1878,6 +1954,10 @@
         GET_INFOVALUE( str_tmp, "status = " );
         if( ! str_tmp.empty() ) m_status = atoi( str_tmp.c_str() );
 
+        // charset
+        GET_INFOVALUE( str_tmp, "charset = " );
+        if( ! str_tmp.empty() ) set_charcode( MISC::charcode_from_cstr( str_tmp.c_str() ) );
+
         // あぼーん ID
         GET_INFOVALUE( str_tmp, "aboneid = " );
         if( ! str_tmp.empty() ) m_list_abone_id = MISC::strtolist( str_tmp );
@@ -1960,6 +2040,16 @@
         GET_INFOVALUE( str_tmp, "aboneage = " );
         if( ! str_tmp.empty() ) m_abone_age = atoi( str_tmp.c_str() );
 
+        // デフォルト名無しあぼーん
+        m_abone_default_name = false;
+        GET_INFOVALUE( str_tmp, "abonedefaultname = " );
+        if( ! str_tmp.empty() ) m_abone_default_name = atoi( str_tmp.c_str() );
+
+        // ID無しあぼーん
+        m_abone_noid = false;
+        GET_INFOVALUE( str_tmp, "abonenoid = " );
+        if( ! str_tmp.empty() ) m_abone_noid = atoi( str_tmp.c_str() );
+
         // 最終更新チェック時間
         GET_INFOVALUE( str_tmp, "checktime = " );
         if( ! str_tmp.empty() ){
@@ -2032,6 +2122,7 @@
               << "org_host = " << m_org_host << std::endl
               << "load = " << m_number_load << std::endl
               << "seen = " << m_number_seen << std::endl
+              << "max = " << m_number_max << std::endl
               << "modified = " << m_date_modified << std::endl
               << "writetime = " << m_write_time_date << std::endl
               << "writename = " << m_write_name << std::endl
@@ -2039,6 +2130,7 @@
               << "writefixname = " << m_write_fixname << std::endl
               << "writefixmail = " << m_write_fixmail << std::endl
               << "status = " << m_status << std::endl
+              << "charcode = " << get_charcode() <<std::endl
               << "transparent_abone = " << m_abone_transparent << std::endl
               << "bookmarked_thread = " << m_bookmarked_thread << std::endl
     ;
@@ -2140,6 +2232,7 @@
          << "org_host = " << m_org_host << std::endl
          << "load = " << m_number_load << std::endl
          << "seen = " << m_number_seen << std::endl
+         << "max = " << m_number_max << std::endl
          << "modified = " << m_date_modified << std::endl
          << "access = " << get_access_time_str() << std::endl
          << "writetime = " << ss_write.str() << std::endl
@@ -2148,6 +2241,7 @@
          << "writefixname = " << m_write_fixname << std::endl
          << "writefixmail = " << m_write_fixmail << std::endl
          << "status = " << m_status << std::endl
+         << "charset = " << MISC::charcode_to_cstr( get_charcode() ) << std::endl
          << "aboneid = " << str_abone_id << std::endl
          << "abonename = " << str_abone_name << std::endl
          << "bookmark = " << ss_bookmark.str() << std::endl
@@ -2159,6 +2253,8 @@
          << "bkmark_thread = " << m_bookmarked_thread << std::endl
          << "posted = " << ss_posted.str() << std::endl
          << "aboneage = " << m_abone_age << std::endl
+         << "abonedefaultname = " << m_abone_default_name << std::endl
+         << "abonenoid = " << m_abone_noid << std::endl
          << "checktime = " << ss_check.str() << std::endl
          << "aboneboard = " << m_abone_board << std::endl
          << "aboneglobal = " << m_abone_global << std::endl
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlebase.h jd-2.8.9-a171004/src/dbtree/articlebase.h
--- jd-2.8.9-150226/src/dbtree/articlebase.h	2014-04-13 05:08:36.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlebase.h	2017-03-28 23:30:14.115816000 +0900
@@ -19,10 +19,12 @@
 
 #include "jdlib/constptr.h"
 
+#include "charcode.h"
+
 namespace DBTREE
 {
     class NodeTreeBase;
-    class NODE;
+    struct NODE;
 
     class ArticleBase : public SKELETON::Lockable
     {
@@ -47,11 +49,14 @@
         std::string m_ext_err;       // HTTPコード以外のエラーメッセージ
         int m_status;                // 状態 ( global.h で定義 )
 
+        CharCode m_charcode;         // 文字エンコーディング
+
         // 移転する前にこのスレがあった旧ホスト名( 移転していないなら m_url に含まれているホスト名と同じ )
         // 詳しくはコンストラクタの説明を参照せよ
         std::string m_org_host;
 
         std::string m_subject;       // サブジェクト
+        std::string m_modified_subject; // 置換で変更されたサブジェクト
         int m_number;                // サーバ上にあるレスの数
         int m_number_diff;            // レス増分( subject.txt をロードした時の m_number の増分 )
         int m_number_new;            // 新着数( ロードした時の差分読み込み数)
@@ -78,6 +83,8 @@
         bool m_abone_transparent; // 透明あぼーん
         bool m_abone_chain; // 連鎖あぼーん
         bool m_abone_age; // age ているレスをあぼーん
+        bool m_abone_default_name; // デフォルト名無しをあぼーん
+        bool m_abone_noid; // ID無しをあぼーん
         bool m_abone_board; // 板レベルでのあぼーんを有効にする
         bool m_abone_global; // 全体レベルでのあぼーんを有効にする
 
@@ -118,7 +125,7 @@
 
       public:
 
-        ArticleBase( const std::string& datbase, const std::string& id, bool cached );
+        ArticleBase( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode );
         virtual ~ArticleBase();
 
         const bool empty();
@@ -141,13 +148,17 @@
         const std::string& get_id() const { return m_id; }
         const std::string& get_key() const { return m_key; }
         const std::string& get_subject() const { return m_subject; }
+        const std::string& get_modified_subject( const bool renew = false );
         const int get_number() const { return m_number; }
         const int get_number_diff() const { return m_number_diff; }
         const int get_number_new() const { return m_number_new; }
         const int get_number_load() const { return m_number_load; }
-        const int get_number_seen() const{  return m_number_seen; }
+        const int get_number_seen() const { return m_number_seen; }
+        const int get_number_max() const { return m_number_max; }
 
-        void set_number_max( const int number ){ m_number_max = number; }
+        // 文字エンコーディング
+        const CharCode get_charcode() const { return m_charcode; }
+        void set_charcode( const CharCode charcode ){ m_charcode = charcode; }
 
         // スレ速度
         const int get_speed();
@@ -212,9 +223,6 @@
         // ref == true なら先頭に ">" を付ける        
         const std::string get_res_str( int number, bool ref = false );
 
-        // number 番のレスの生文字列を返す
-        const std::string get_raw_res_str( int number );
-
         // 書き込み時の名前とメアド
         const std::string& get_write_name() const { return m_write_name; }
         void set_write_name( const std::string& str ){ m_save_info = true; m_write_name = str; }
@@ -290,6 +298,7 @@
         void set_number( const int number, const bool is_online );
         void set_number_load( const int number_load );
         void set_number_seen( const int number_seen );
+        void set_number_max( const int number_max );
         void update_writetime();
 
         // キャッシュ削除
@@ -328,6 +337,12 @@
         // ageあぼーん
         const bool get_abone_age() const { return m_abone_age; }
 
+        // デフォルト名無しあぼーん
+        const bool get_abone_default_name() const { return m_abone_default_name; }
+
+        // ID無しあぼーん
+        const bool get_abone_noid() const { return m_abone_noid; }
+
         // 板レベルでのあぼーん
         const bool get_abone_board() const { return m_abone_board; }
 
@@ -347,6 +362,7 @@
                           const std::list< std::string >& regexs,
                           const std::vector< char >& vec_abone_res,
                           const bool transparent, const bool chain, const bool age,
+                          const bool default_name, const bool noid,
                           const bool board, const bool global
             );
 
@@ -358,6 +374,8 @@
         void set_abone_transparent( const bool set ); // 透明
         void set_abone_chain( const bool set ); // 連鎖
         void set_abone_age( const bool set ); // age
+        void set_abone_default_name( const bool set ); // デフォルト名無し
+        void set_abone_noid( const bool set ); // ID無し
         void set_abone_board( const bool set ); // 板レベルでのあぼーん
         void set_abone_global( const bool set ); // 全体レベルでのあぼーん
 
@@ -378,8 +396,8 @@
         // キャッシュがあって、force = true の時は強制書き込み
         virtual void save_info( const bool force );
 
-        const bool is_loading(); // ロード中か
-        const bool is_checking_update();  // 更新チェック中か
+        const bool is_loading() const; // ロード中か
+        const bool is_checking_update() const;  // 更新チェック中か
 
         // スレッドのロード停止
         void stop_load();
@@ -401,6 +419,9 @@
         // スレッド924か
         const bool is_924() const{ return m_924; }
 
+        // NodeTree削除
+        virtual void unlock_impl();
+
       private:
 
         // 更新チェック可能
@@ -416,7 +437,6 @@
 
         void slot_node_updated();
         void slot_load_finished();
-        virtual void unlock_impl();
 
         // お気に入りのアイコンとスレビューのタブのアイコンに更新マークを表示
         // update == true の時に表示。falseなら戻す
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlejbbs.cpp jd-2.8.9-a171004/src/dbtree/articlejbbs.cpp
--- jd-2.8.9-150226/src/dbtree/articlejbbs.cpp	2013-12-29 20:19:15.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlejbbs.cpp	2016-02-08 22:27:46.708497000 +0900
@@ -14,8 +14,8 @@
 
 using namespace DBTREE;
 
-ArticleJBBS::ArticleJBBS( const std::string& datbase, const std::string& _id, bool cached )
-    : ArticleBase( datbase, _id, cached )
+ArticleJBBS::ArticleJBBS( const std::string& datbase, const std::string& _id, bool cached, const CharCode charcode )
+    : ArticleBase( datbase, _id, cached, charcode )
 {
     assert( ! get_id().empty() );
 
@@ -35,10 +35,8 @@
 {
     if( msg.empty() ) return std::string();
 
-    std::string charset = DBTREE::board_charset( get_url() );
-
     // DIR と BBS を分離する( ID = DIR/BBS )
-    std::string boardid = DBTREE::board_id( get_url() );
+    const std::string& boardid = DBTREE::board_id( get_url() );
     int i = boardid.find( "/" );
     std::string dir = boardid.substr( 0, i );
     std::string bbs = boardid.substr( i + 1 );
@@ -49,10 +47,10 @@
             << "&KEY="     << get_key()
             << "&DIR="     << dir
             << "&TIME="    << get_time_modified()
-            << "&submit="  << MISC::charset_url_encode( "書き込む", charset )
-            << "&NAME="    << MISC::charset_url_encode( name, charset )
-            << "&MAIL="    << MISC::charset_url_encode( mail, charset )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, charset );
+            << "&submit="  << MISC::url_encode( std::string( "書き込む" ), get_charcode() )
+            << "&NAME="    << MISC::url_encode( name, get_charcode() )
+            << "&MAIL="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() );
 
 #ifdef _DEBUG
     std::cout << "Articlejbbs::create_write_message " << ss_post.str() << std::endl;
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlejbbs.h jd-2.8.9-a171004/src/dbtree/articlejbbs.h
--- jd-2.8.9-150226/src/dbtree/articlejbbs.h	2010-02-21 00:33:54.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlejbbs.h	2015-12-31 11:35:32.239321000 +0900
@@ -17,7 +17,7 @@
     {
       public:
 
-        ArticleJBBS( const std::string& datbase, const std::string& id, bool cached );
+        ArticleJBBS( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode );
         ~ArticleJBBS();
 
         // 書き込みメッセージ変換
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlelocal.cpp jd-2.8.9-a171004/src/dbtree/articlelocal.cpp
--- jd-2.8.9-150226/src/dbtree/articlelocal.cpp	2009-04-29 22:46:15.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlelocal.cpp	2015-12-31 11:35:32.383322000 +0900
@@ -10,8 +10,8 @@
 using namespace DBTREE;
 
 
-ArticleLocal::ArticleLocal( const std::string& datbase, const std::string& id )
-    : Article2chCompati( datbase, id, true )
+ArticleLocal::ArticleLocal( const std::string& datbase, const std::string& id, const CharCode charcode )
+    : Article2chCompati( datbase, id, true, charcode )
 {
 #ifdef _DEBUG
     std::cout << "ArticleLocal::ArticleLocal datbase = " << datbase
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlelocal.h jd-2.8.9-a171004/src/dbtree/articlelocal.h
--- jd-2.8.9-150226/src/dbtree/articlelocal.h	2009-03-21 00:04:16.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlelocal.h	2015-12-31 11:35:32.287322000 +0900
@@ -15,7 +15,7 @@
     {
       public:
 
-        ArticleLocal( const std::string& datbase, const std::string& id );
+        ArticleLocal( const std::string& datbase, const std::string& id, const CharCode charcode );
         ~ArticleLocal();
 
         // ID がこのスレのものかどうか
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlemachi.cpp jd-2.8.9-a171004/src/dbtree/articlemachi.cpp
--- jd-2.8.9-150226/src/dbtree/articlemachi.cpp	2010-02-21 00:33:54.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlemachi.cpp	2015-12-31 12:40:22.719262000 +0900
@@ -17,8 +17,8 @@
 using namespace DBTREE;
 
 
-ArticleMachi::ArticleMachi( const std::string& datbase, const std::string& _id, bool cached )
-    : ArticleBase( datbase, _id, cached )
+ArticleMachi::ArticleMachi( const std::string& datbase, const std::string& _id, bool cached, const CharCode charcode )
+    : ArticleBase( datbase, _id, cached, charcode )
 {
     assert( !get_id().empty() );
 
@@ -38,17 +38,15 @@
 {
     if( msg.empty() ) return std::string();
 
-    std::string charset = DBTREE::board_charset( get_url() );
-
     std::stringstream ss_post;
     ss_post.clear();
     ss_post << "BBS="      << DBTREE::board_id( get_url() )
             << "&KEY="     << get_key()
             << "&TIME="    << get_time_modified()
-            << "&submit="  << MISC::charset_url_encode( "書き込む", charset )
-            << "&NAME="    << MISC::charset_url_encode( name, charset )
-            << "&MAIL="    << MISC::charset_url_encode( mail, charset )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, charset );
+            << "&submit="  << MISC::url_encode( std::string( "書き込む" ), get_charcode() )
+            << "&NAME="    << MISC::url_encode( name, get_charcode() )
+            << "&MAIL="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() );
 
 #ifdef _DEBUG
     std::cout << "ArticleMachi::create_write_message " << ss_post.str() << std::endl;
diff -aurNZ jd-2.8.9-150226/src/dbtree/articlemachi.h jd-2.8.9-a171004/src/dbtree/articlemachi.h
--- jd-2.8.9-150226/src/dbtree/articlemachi.h	2010-02-21 00:33:54.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/articlemachi.h	2016-02-16 00:30:27.669014000 +0900
@@ -5,7 +5,7 @@
 //
 
 #ifndef _ARTICLEMACHI_H
-#define _ARTICLEMACHI_H_H
+#define _ARTICLEMACHI_H
 
 #include "articlebase.h"
 
@@ -17,7 +17,7 @@
     {
       public:
 
-        ArticleMachi( const std::string& datbase, const std::string& id, bool cached );
+        ArticleMachi( const std::string& datbase, const std::string& id, bool cached, const CharCode charcode );
         ~ArticleMachi();
 
         // 書き込みメッセージ変換
diff -aurNZ jd-2.8.9-150226/src/dbtree/board2ch.cpp jd-2.8.9-a171004/src/dbtree/board2ch.cpp
--- jd-2.8.9-150226/src/dbtree/board2ch.cpp	2011-03-04 00:09:11.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/board2ch.cpp	2017-03-28 23:31:41.051815465 +0900
@@ -12,6 +12,7 @@
 #include "jdlib/miscutil.h"
 #include "jdlib/misctime.h"
 #include "jdlib/jdregex.h"
+#include "jdlib/loaderdata.h"
 
 #include "login2ch.h"
 #include "loginbe.h"
@@ -69,7 +70,10 @@
 {
     const int mode = get_mode_local_proxy();
 
-    if( mode == DBTREE::PROXY_GLOBAL ) return CONFIG::get_proxy_port_for2ch();
+    if( mode == DBTREE::PROXY_GLOBAL ){
+
+        if( CONFIG::get_use_proxy_for2ch() ) return CONFIG::get_proxy_port_for2ch();
+    }
     else if( mode == DBTREE::PROXY_LOCAL ) return get_local_proxy_port();
 
     return 0;
@@ -180,7 +184,7 @@
     std::stringstream ss_post;
     ss_post.clear();
     ss_post << "bbs="      << get_id()
-            << "&subject=" << MISC::charset_url_encode( subject, get_charset() );
+            << "&subject=" << MISC::url_encode( subject, get_charcode() );
 
     // キーワード( hana=mogera や suka=pontan など )
     const std::string keyword = get_keyword_for_write();
@@ -194,14 +198,14 @@
     }
 
     ss_post << "&time="    << get_time_modified()
-            << "&submit="  << MISC::charset_url_encode( "新規スレッド作成", get_charset() )
-            << "&FROM="    << MISC::charset_url_encode( name, get_charset() )
-            << "&mail="    << MISC::charset_url_encode( mail, get_charset() )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, get_charset() );
+            << "&submit="  << MISC::url_encode( std::string( "新規スレッド作成" ), get_charcode() )
+            << "&FROM="    << MISC::url_encode( name, get_charcode() )
+            << "&mail="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() );
 
     if( CORE::get_loginp2()->login_now() ){
 
-        ss_post << "&detect_hint=" << MISC::charset_url_encode( "◎◇", get_charset() )
+        ss_post << "&detect_hint=" << MISC::url_encode( std::string( "◎◇" ), get_charcode() )
                 << "&host=" << MISC::url_encode( MISC::get_hostname( get_root(), false ) )
                 << "&key="
                 << "&popup=1"
@@ -256,12 +260,9 @@
 {
     if( empty() ) return get_article_null();
 
-    ArticleBase* article = new DBTREE::Article2ch( datbase, id, cached );
+    ArticleBase* article = new DBTREE::Article2ch( datbase, id, cached, get_charcode() );
     if( article ){
         get_hash_article()->push( article );
-
-        // 最大レス数セット
-        article->set_number_max( get_number_max_res() );
     }
     else return get_article_null();
 
@@ -332,3 +333,75 @@
         }
     }
 }
+
+
+
+//
+// ロード用データ作成
+//
+void Board2ch::create_loaderdata( JDLIB::LOADERDATA& data )
+{
+#if 0
+    const std::string& root( get_root() );
+    size_t pos;
+
+    // スレのリストをAPIサーバから取得する
+    //
+    // XXX サーバー移転の後で302を返さないので移転を検出できない
+
+    if( ! is_dedicated_server() && CORE::get_login2ch()->login_now()
+            && ( pos = root.find( "://" ) ) != std::string::npos ){
+        pos += 3;
+
+        data.url = CONFIG::get_url_api2ch() + "/subject/"
+            + root.substr( pos, root.find_first_of( '.', pos ) - pos )
+            + get_path_board() + "/";
+
+        if( ! CONFIG::get_x_2ch_ua().empty() )
+            data.ex_field = "X-2ch-UA: " + CONFIG::get_x_2ch_ua() + "\r\n";
+
+        data.agent = CONFIG::get_agent_for2ch();
+        if( CONFIG::get_use_proxy_for2ch() ){
+            data.host_proxy = CONFIG::get_proxy_for2ch();
+            data.port_proxy = CONFIG::get_proxy_port_for2ch();
+        }
+        data.basicauth_proxy = CONFIG::get_proxy_basicauth_for2ch();
+    }
+    else
+#endif
+    {
+        data.url = url_subject();
+
+        data.agent = get_agent();
+        data.host_proxy = get_proxy_host();
+        data.port_proxy = get_proxy_port();
+        data.basicauth_proxy = get_proxy_basicauth();
+    }
+
+    data.size_buf = CONFIG::get_loader_bufsize_board();
+    data.timeout = CONFIG::get_loader_timeout();
+    data.modified = get_date_modified();
+
+#ifdef _DEBUG
+    std::cout << "Board2ch::create_loadrdata " << data.url << std::endl;
+#endif
+
+}
+
+
+//
+// 特殊な2chサーバか
+//
+const bool Board2ch::is_dedicated_server() const
+{
+    bool ret = false;
+
+    std::string hostname = MISC::get_hostname( get_root(), false );
+    std::list< std::string > list_server = MISC::split_line( CONFIG::get_dedicated_server() );
+    std::list< std::string >::iterator it = list_server.begin();
+    for( ; it != list_server.end(); ++it ){
+        if( *it == hostname ){ ret = true; break; }
+    }
+
+    return ret;
+}
diff -aurNZ jd-2.8.9-150226/src/dbtree/board2ch.h jd-2.8.9-a171004/src/dbtree/board2ch.h
--- jd-2.8.9-150226/src/dbtree/board2ch.h	2011-03-04 00:09:11.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/board2ch.h	2016-01-24 02:33:55.284199069 +0900
@@ -57,6 +57,9 @@
         // datの最大サイズ(Kバイト)
         virtual const int get_max_dat_lng() const { return DEFAULT_MAX_DAT_LNG; }
 
+        // 特殊な2chサーバか
+        virtual const bool is_dedicated_server() const;
+
       protected:
 
         // クッキー:HAP
@@ -71,6 +74,8 @@
         // デフォルト最大レス数
         virtual const int get_default_number_max_res() { return DEFAULT_NUMBER_MAX_2CH; }
 
+        virtual void create_loaderdata( JDLIB::LOADERDATA& data );
+
         virtual ArticleBase* append_article( const std::string& datbase, const std::string& id, const bool cached );
     };
 }
diff -aurNZ jd-2.8.9-150226/src/dbtree/board2chcompati.cpp jd-2.8.9-a171004/src/dbtree/board2chcompati.cpp
--- jd-2.8.9-150226/src/dbtree/board2chcompati.cpp	2015-01-29 21:54:00.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/board2chcompati.cpp	2017-03-28 23:33:33.051813748 +0900
@@ -9,6 +9,7 @@
 #include "settingloader.h"
 #include "ruleloader.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/miscmsg.h"
 #include "jdlib/jdregex.h"
@@ -47,7 +48,7 @@
     set_subjecttxt( "subject.txt" );
     set_ext( ".dat" );
     set_id( path_board.substr( 1 ) ); // 先頭の '/' を除く
-    set_charset( "MS932" );
+    set_charcode( CHARCODE_SJIS );
 
     BoardBase::set_basicauth( basicauth );
 }
@@ -75,8 +76,10 @@
 //
 const bool Board2chCompati::is_valid( const std::string& filename )
 {
-    if( filename.find( get_ext() ) == std::string::npos ) return false;
-    if( filename.length() - filename.rfind( get_ext() ) != get_ext().length() ) return false;
+    size_t lng_filename = filename.length();
+    size_t lng_ext = get_ext().length();
+    if( lng_ext != 0 && ( lng_filename <= lng_ext ||
+            filename.compare( lng_filename - lng_ext, lng_ext, get_ext() ) ) ) return false;
 
     size_t dig, n;
     MISC::str_to_uint( filename.c_str(), dig, n );
@@ -97,91 +100,28 @@
     std::cout << "Board2chCompati::cookie_for_write\n";
 #endif
 
-    JDLIB::Regex regex;
-    const size_t offset = 0;
-    const bool icase = false;
-    const bool newline = true;
-    const bool usemigemo = false;
-    const bool wchar = false;
-
-    std::string cookie_expire;
-    std::string cookie_path;
-    std::string cookie_pon;
     std::string cookie_hap = get_hap();
-    std::string cookie_name;
-    std::string cookie_mail;
-
-    const std::string query_expire = "expires=([^;]*)";
-    const std::string query_path = "path=([^;]*)";
-    const std::string query_pon = "PON=([^;]*)?";
-    const std::string query_hap = "HAP=([^;]*)?";
-    const std::string query_name = "NAME=([^;]*)?";
-    const std::string query_mail = "MAIL=([^;]*)?";
-
-    bool use_pon = false;
-    bool use_hap = ! cookie_hap.empty();
-    bool use_name = false;
-    bool use_mail = false;
-
+    std::string cookie;
     std::list< std::string >::const_iterator it = list_cookies.begin();
 
-    // expire と path は一つ目のcookieから取得
-    if( regex.exec( query_expire, (*it), offset, icase, newline, usemigemo, wchar ) ) cookie_expire = regex.str( 1 );
-    if( regex.exec( query_path, (*it), offset, icase, newline, usemigemo, wchar ) ) cookie_path = regex.str( 1 );
-
     for( ; it != list_cookies.end(); ++it ){
+        const std::string tmp_cookie = MISC::Iconv( (*it), get_charcode(), CHARCODE_UTF8 );
 
-        const std::string tmp_cookie = MISC::Iconv( (*it), get_charset(), "UTF-8" );
-
-#ifdef _DEBUG
-        std::cout << tmp_cookie << std::endl;
-#endif
+        if( ! cookie_hap.empty() && tmp_cookie.compare( 0, 4, "HAP=" ) == 0 ) continue;
 
-        if( regex.exec( query_pon, tmp_cookie, offset, icase, newline, usemigemo, wchar ) ){
-            use_pon = true;
-            cookie_pon = regex.str( 1 );
-        }
-        if( ! use_hap && regex.exec( query_hap, tmp_cookie, offset, icase, newline, usemigemo, wchar ) ){
-            use_hap = true;
-            cookie_hap = regex.str( 1 );
-        }
-        if( regex.exec( query_name, tmp_cookie, offset, icase, newline, usemigemo, wchar ) ){
-            use_name = true;
-            cookie_name = MISC::charset_url_encode( regex.str( 1 ), get_charset() );
-        }
-        if( regex.exec( query_mail, tmp_cookie, offset, icase, newline, usemigemo, wchar ) ){
-            use_mail = true;
-            cookie_mail = MISC::charset_url_encode( regex.str( 1 ), get_charset() );
-        }
+        if( ! cookie.empty() ) cookie += "; ";
+        if( tmp_cookie.find_first_of( ';' ) == std::string::npos )
+            cookie += tmp_cookie;
+        else
+            cookie += tmp_cookie.substr( 0, tmp_cookie.find_first_of( ';' ) );
     }
 
-    // PONを取得していないときはHAPを送らない
-    if( ! use_pon || cookie_pon.empty() ){
-        use_hap = false;
-        cookie_hap = std::string();
+    if( ! cookie_hap.empty() ){
+        if( ! cookie.empty() ) cookie += "; ";
+        cookie += "HAP=" + cookie_hap;
     }
 
 #ifdef _DEBUG
-    std::cout << "expire = " << cookie_expire << std::endl
-              << "path = " << cookie_path << std::endl    
-              << "pon = " << cookie_pon << " " << use_pon << std::endl
-              << "hap = " << cookie_hap << " " << use_hap << std::endl
-              << "name = " << cookie_name << " " << use_name << std::endl
-              << "mail = " << cookie_mail << " " << use_mail << std::endl;
-#endif    
-
-    std::string cookie;
-
-    if( use_pon ) cookie += "PON=" + cookie_pon + "; ";
-    if( use_hap ) cookie += "HAP=" + cookie_hap + "; ";
-    if( use_name ) cookie += "NAME=" + cookie_name + "; ";
-    if( use_mail ) cookie += "MAIL=" + cookie_mail + "; ";
-
-    if( cookie.empty() ) return std::string();
-
-    cookie += "expires=" + cookie_expire + "; path=" + cookie_path;
-
-#ifdef _DEBUG
     std::cout << "cookie = " << cookie << std::endl;
 #endif 
 
@@ -240,7 +180,7 @@
 
         // キーワード取得
         if( ! keyword.empty() ) keyword += "&";
-        keyword += MISC::charset_url_encode( name, get_charset() ) + "=" + MISC::charset_url_encode( value, get_charset() );
+        keyword += MISC::url_encode( name, get_charcode() ) + "=" + MISC::url_encode( value, get_charcode() );
     }
 
 #ifdef _DEBUG
@@ -261,12 +201,12 @@
     std::stringstream ss_post;
     ss_post.clear();
     ss_post << "bbs="      << get_id()
-            << "&subject=" << MISC::charset_url_encode( subject, get_charset() )
+            << "&subject=" << MISC::url_encode( subject, get_charcode() )
             << "&time="    << get_time_modified()
-            << "&submit="  << MISC::charset_url_encode( "新規スレッド作成", get_charset() )
-            << "&FROM="    << MISC::charset_url_encode( name, get_charset() )
-            << "&mail="    << MISC::charset_url_encode( mail, get_charset() )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, get_charset() );
+            << "&submit="  << MISC::url_encode( std::string( "新規スレッド作成" ), get_charcode() )
+            << "&FROM="    << MISC::url_encode( name, get_charcode() )
+            << "&mail="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() );
 
 #ifdef _DEBUG
     std::cout << "Board2chCompati::create_newarticle_message " << ss_post.str() << std::endl;
@@ -312,12 +252,9 @@
 {
     if( empty() ) return get_article_null();
 
-    ArticleBase* article = new DBTREE::Article2chCompati( datbase, id, cached );
+    ArticleBase* article = new DBTREE::Article2chCompati( datbase, id, cached, get_charcode() );
     if( article ){
         get_hash_article()->push( article );
-
-        // 最大レス数セット
-        article->set_number_max( get_number_max_res() );
     }
     else return get_article_null();
     
@@ -366,13 +303,9 @@
         }
 
         // subject取得
-        bool exist_amp = false;
         ++pos;
         str_subject = pos;
-        while( *pos != '\0' && *pos != '\n' ){
-            if( *pos == '&' ) exist_amp = true;
-            ++pos;
-        }
+        while( *pos != '\0' && *pos != '\n' ) ++pos;
         --pos;
         while( *pos != '(' && *pos != '\n' && pos != str_subject ) --pos;
         
@@ -404,16 +337,8 @@
 
         artinfo.id.assign( str_id_dat, lng_id_dat );
 
-        if( str_subject[ lng_subject-1 ] == ' ' ){
-            lng_subject--;  // 2chのsubject.txtは()の前に空白が一つ入る
-        }
         artinfo.subject.assign( str_subject, lng_subject );
 
-        if( exist_amp ){
-            artinfo.subject = MISC::replace_str( artinfo.subject, "&lt;", "<" );
-            artinfo.subject = MISC::replace_str( artinfo.subject, "&gt;", ">" );
-        }
-        
         artinfo.number = atol( str_num );
 
         get_list_artinfo().push_back( artinfo );
@@ -496,6 +421,19 @@
 }
 
 
+
+//
+// SETTING.TXTのURL
+//
+// (例) "http://www.hoge2ch.net/hogeboard/SETTING.TXT"
+//
+//
+const std::string Board2chCompati::url_settingtxt()
+{
+    return url_boardbase() + SETTING_TXT;
+}
+
+
 const std::string Board2chCompati::settingtxt()
 {
     if( m_settingloader ){
@@ -559,10 +497,10 @@
 #endif
 
     if( ! m_ruleloader ) m_ruleloader = new RuleLoader( url_boardbase() );
-    m_ruleloader->load_text();
+    m_ruleloader->load_text( get_charcode() );
 
     if( ! m_settingloader ) m_settingloader = new SettingLoader( url_boardbase() );
-    m_settingloader->load_text();
+    m_settingloader->load_text( get_charcode() );
 }
 
 
@@ -578,17 +516,25 @@
 #endif
 
     if( ! m_ruleloader ) m_ruleloader = new RuleLoader( url_boardbase() );
-    m_ruleloader->download_text();
+    m_ruleloader->download_text( get_charcode() );
 
     if( ! m_settingloader ) m_settingloader = new SettingLoader( url_boardbase() );
-    m_settingloader->download_text();
+    m_settingloader->download_text( get_charcode() );
 }
 
 
 //
-// レス数であぼーん(グローバル)
+// レス数(最小)であぼーん(グローバル)
+//
+const int Board2chCompati::get_abone_min_number_global()
+{
+    return CONFIG::get_abone_min_number_thread();
+}
+
+//
+// レス数(最大)であぼーん(グローバル)
 //
-const int Board2chCompati::get_abone_number_global()
+const int Board2chCompati::get_abone_max_number_global()
 {
-    return CONFIG::get_abone_number_thread();
+    return CONFIG::get_abone_max_number_thread();
 }
diff -aurNZ jd-2.8.9-150226/src/dbtree/board2chcompati.h jd-2.8.9-a171004/src/dbtree/board2chcompati.h
--- jd-2.8.9-150226/src/dbtree/board2chcompati.h	2011-02-13 02:12:46.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/board2chcompati.h	2016-05-11 03:02:47.606103000 +0900
@@ -43,6 +43,8 @@
         // ローカルルール
         virtual const std::string localrule();
 
+        // SETTING.TXT のURL
+        virtual const std::string url_settingtxt();
         // SETTING.TXT 
         virtual const std::string settingtxt();
         virtual const std::string default_noname();
@@ -62,7 +64,8 @@
         virtual void download_rule_setting();
 
         // レス数であぼーん(グローバル)
-        virtual const int get_abone_number_global();
+        virtual const int get_abone_min_number_global();
+        virtual const int get_abone_max_number_global();
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardbase.cpp jd-2.8.9-a171004/src/dbtree/boardbase.cpp
--- jd-2.8.9-150226/src/dbtree/boardbase.cpp	2014-03-29 15:04:35.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardbase.cpp	2017-03-31 03:41:06.420937064 +0900
@@ -13,8 +13,10 @@
 
 #include "jdlib/jdiconv.h"
 #include "jdlib/jdregex.h"
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/miscmsg.h"
+#include "jdlib/misctime.h"
 #include "jdlib/loaderdata.h"
 #include "jdlib/confloader.h"
 
@@ -39,7 +41,7 @@
 
 enum
 {
-    SIZE_OF_RAWDATA = 2 * 1024 * 1024
+    SIZE_OF_RAWDATA = 256 * 1024
 };
 
 using namespace DBTREE;
@@ -62,7 +64,7 @@
     , m_number_max_res( 0 )
     , m_iconv( NULL )
     , m_rawdata( NULL )
-    , m_rawdata_left( NULL )
+    , m_charcode_bak( CHARCODE_UNKNOWN )
     , m_read_info( 0 )
     , m_append_articles( false )
     , m_get_article( NULL )
@@ -112,7 +114,7 @@
 
 ArticleBase* BoardBase::get_article_null()
 {
-    if( ! m_article_null ) m_article_null = new DBTREE::ArticleBase( "", "", false );
+    if( ! m_article_null ) m_article_null = new DBTREE::ArticleBase( "", "", false, get_charcode() );
     return m_article_null;
 }
 
@@ -128,8 +130,8 @@
 //
 bool BoardBase::equal( const std::string& url )
 {
-    if( url.find( get_root() ) == 0
-        && url.find( get_path_board() + "/" ) != std::string::npos ) return true;
+    if( url.compare( 0, get_root().length(), get_root() ) == 0
+        && url.find( get_path_board() + "/", get_root().length() ) != std::string::npos ) return true;
 
     return false;
 }
@@ -292,6 +294,8 @@
     std::cout << "BoardBase::set_list_cookies_for_write\n";
 #endif
 
+    time_t ct = time(0);
+
     std::list< std::string >::const_iterator it = list_cookies.begin();
     for( ; it != list_cookies.end(); ++it ){
 
@@ -302,12 +306,25 @@
 #endif
 
         std::string key;
-        const size_t n = cookie.find( "=" );
+        const size_t n = cookie.find_first_of( '=' );
         if( n != std::string::npos ) key = cookie.substr( 0, n+1 );
 
+        // expiresを取得
+        std::string expire;
+        time_t extime( 0x7FFFFFFF );
+        size_t pos = cookie.find( "expires=", n );
+        if( pos != std::string::npos ){
+            pos += 8;
+            size_t epos = cookie.find_first_of( ';', pos );
+            extime = MISC::datetotime( cookie.substr( pos, epos - pos ), true );
+#ifdef _DEBUG
+            std::cout << "ct = " << ct << " expires= " << extime << std::endl;
+#endif
+        }
+
         // 更新
         // クッキーの削除は未実装
-        if( ! key.empty() && key.find( ";" ) == std::string::npos ){
+        if( ! key.empty() && key.find( ";" ) == std::string::npos && extime > ct ){
              
 #ifdef _DEBUG        
             std::cout << "key = " << key << std::endl;
@@ -343,9 +360,6 @@
     if( m_rawdata ) free( m_rawdata );
     m_rawdata = NULL;
 
-    if( m_rawdata_left ) free( m_rawdata_left );
-    m_rawdata_left = NULL;
-
     m_lng_rawdata = 0;
     m_lng_rawdata_left = 0;
 
@@ -372,7 +386,7 @@
     // "update_board" コマンドの後に"update_board_item"を送ると
     // ローディングが終了しているため行を二回更新してしまうので注意
     // 詳しくは BoardViewBase::update_item() を参照
-    CORE::core_set_command( "update_board_item", url_subject(),
+    CORE::core_set_command( "update_board_item", url_boardbase(),
                             std::string() // IDとして空文字を送る
         );
 
@@ -495,9 +509,6 @@
 #endif
 
     m_number_max_res = MAX( 0, MIN( MAX_RESNUMBER, number ) );
-
-    ArticleHashIterator it = m_hash_article->begin();
-    for( ; it != m_hash_article->end(); ++it ) ( *it )->set_number_max( m_number_max_res );
 }
 
 
@@ -544,9 +555,9 @@
     m_root = root;
     m_path_board = path_board;
 
-    m_query_dat = std::string();
-    m_query_cgi = std::string();
-    m_query_kako = std::string();
+    m_query_dat.clear();
+    m_query_cgi.clear();
+    m_query_kako.clear();
 
     // modified 時刻をリセット
     // 自動移転処理後に bbsmenu.html を読み込んだときに、bbsmenu.html の
@@ -576,10 +587,6 @@
 
     JDLIB::Regex regex;
     const size_t offset = 0;
-    const bool icase = false;
-    const bool newline = true;
-    const bool usemigemo = false;
-    const bool wchar = false;
 
     std::string id; // スレッドのID
 
@@ -589,30 +596,38 @@
     
     num_from = num_to = 0;
 
-    if( m_query_dat.empty() ){
+    if( ! m_query_dat.compiled() ){
+
+        const bool icase = false;
+        const bool newline = true;
+        const bool usemigemo = false;
+        const bool wchar = false;
 
         // dat 型
         const std::string datpath = MISC::replace_str( url_datpath(), "?", "\\?" );
-        m_query_dat = "^ *(http://.+" + datpath  + ")([1234567890]+" + get_ext() + ") *$";
+        const std::string reg_dat = "^ *(https?://.+" + datpath  + ")([0-9]+" + get_ext() + ") *$";
+        m_query_dat.set( reg_dat, icase, newline, usemigemo, wchar );
 
         // read.cgi型
         const std::string cgipath = MISC::replace_str( url_readcgipath(), "?", "\\?" );
-        m_query_cgi = "^ *(http://.+" + cgipath + ")([1234567890]+)/?r?(l50)?([1234567890]+)?(-)?([1234567890]+)?.*$";
+        const std::string reg_cgi = "^ *(https?://.+" + cgipath + ")([0-9]+)/?r?(l50)?([0-9]+)?(-)?([0-9]+)?.*$";
+        m_query_cgi.set( reg_cgi, icase, newline, usemigemo, wchar );
 
         // 過去ログかどうか
         const std::string pathboard = MISC::replace_str( m_path_board, "?", "\\?" );
-        m_query_kako = "^ *(http://.+)" + pathboard  + "/kako(/[1234567890]+)?/[1234567890]+/([1234567890]+).html *$";
+        const std::string reg_kako = "^ *(https?://.+)" + pathboard  + "/kako(/[0-9]+)?/[0-9]+/([0-9]+).html *$";
+        m_query_kako.set( reg_kako, icase, newline, usemigemo, wchar );
 
 #ifdef _DEBUG
-        std::cout << "query_dat = " << m_query_dat << std::endl;
-        std::cout << "query_cgi = " << m_query_cgi << std::endl;
-        std::cout << "query_kako = " << m_query_kako << std::endl;
+        std::cout << "reg_dat = " << reg_dat << std::endl;
+        std::cout << "reg_cgi = " << reg_cgi << std::endl;
+        std::cout << "reg_kako = " << reg_kako << std::endl;
 #endif
     }
 
-    if( regex.exec( m_query_dat , url, offset, icase, newline, usemigemo, wchar ) ) id = regex.str( 2 );
+    if( regex.match( m_query_dat, url, offset ) ) id = regex.str( 2 );
 
-    else if( regex.exec( m_query_cgi , url, offset, icase, newline, usemigemo, wchar ) ){
+    else if( regex.match( m_query_cgi, url, offset ) ){
 
         id = regex.str( 2 ) + get_ext(); 
 
@@ -644,10 +659,7 @@
     // どちらでもない(スレのURLでない)場合
     else{
 
-#ifdef _DEBUG
-        std::cout << "query_kako = " << m_query_kako << std::endl;
-#endif
-        if( regex.exec( m_query_kako , url, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.match( m_query_kako, url, offset ) ){
 
             std::string url_tmp = regex.str( 1 ) + url_datpath() + regex.str( 3 ) + get_ext();
 #ifdef _DEBUG
@@ -994,7 +1006,7 @@
 void BoardBase::download_subject( const std::string& url_update_view, const bool read_from_cache )
 {
 #ifdef _DEBUG
-    std::cout << "BoardBase::download_subject " << url_subject() << std::endl
+    std::cout << "BoardBase::download_subject " << url_boardbase() << std::endl
               << "url_update_view = " << url_update_view << std::endl
               << "read_from_cache = " << read_from_cache << std::endl
               << "empty = " << empty() << std::endl
@@ -1019,6 +1031,9 @@
 
     m_is_booting = SESSION::is_booting();
 
+    // charcodeを一時保存
+    m_charcode_bak = get_charcode();
+
     // オフライン
     if( ! m_is_online  ){
 
@@ -1079,6 +1094,8 @@
 
     if( ! m_rawdata ) m_rawdata = ( char* )malloc( SIZE_OF_RAWDATA );
 
+    // XXX バッファから溢れる分は捨てる
+    if( size >= ( SIZE_OF_RAWDATA - m_lng_rawdata ) ) size = SIZE_OF_RAWDATA - m_lng_rawdata - 1;
     memcpy( m_rawdata + m_lng_rawdata , data, size );
     m_lng_rawdata += size;
     m_rawdata[ m_lng_rawdata ] = '\0';
@@ -1090,24 +1107,21 @@
     // 改行ごとに区切ってUTF8に文字コード変換して解析
     //
 
-    if( ! m_rawdata_left ) m_rawdata_left = ( char* )malloc( SIZE_OF_RAWDATA );
-    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( m_charset, "UTF-8" );
-
-    memcpy( m_rawdata_left + m_lng_rawdata_left, data, size );
+    if( get_code() != HTTP_OK ) set_charcode( m_charcode_bak );
+    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( get_charcode(), CHARCODE_UTF8 );
 
-    size_t byte_in = size + m_lng_rawdata_left;
-    m_lng_rawdata_left = byte_in;
-    while( byte_in && m_rawdata_left[ byte_in -1 ] != '\n' ) --byte_in;
+    size_t byte_in = m_lng_rawdata - m_lng_rawdata_left;
+    const char* pos_left = m_rawdata + m_lng_rawdata_left;
+    while( byte_in && *( pos_left + byte_in - 1 ) != '\n' ) --byte_in;
     if( byte_in ){
 
         int byte_out;
-        const char* rawdata_utf8 = m_iconv->convert( m_rawdata_left,  byte_in,  byte_out );
+        const char* subjects = m_iconv->convert( pos_left,  byte_in,  byte_out );
 
-        parse_subject( rawdata_utf8 );
+        parse_subject( subjects );
 
-        // 残りを先頭に移動
-        m_lng_rawdata_left -= byte_in;
-        memmove( m_rawdata_left, m_rawdata_left + byte_in, m_lng_rawdata_left );
+        // 残りの先頭を更新
+        m_lng_rawdata_left += byte_in;
 
 #ifdef _DEBUG
         std::cout << "BoardBase::receive_data lng_rawdata = " << m_lng_rawdata << " size = " << size
@@ -1149,7 +1163,7 @@
         set_date_modified( std::string() );
         send_update_board();
 
-        if( m_lng_rawdata && get_code() == HTTP_OK && std::string( m_rawdata ).find( "window.location.href" ) != std::string::npos ){
+        if( m_lng_rawdata && get_code() == HTTP_OK && strstr( m_rawdata, "window.location.href" ) != NULL ){
 
 #ifdef _DEBUG
             std::cout << m_rawdata << std::endl;
@@ -1164,7 +1178,13 @@
             std::string query = ".*window.location.href=\"([^\"]*)\".*";
             if( regex.exec( query, m_rawdata, offset, icase, newline, usemigemo, wchar ) ){
 
-                const std::string new_url = regex.str( 1 );
+                std::string new_url = regex.str( 1 );
+                if( new_url.compare( 0, 2, "//" ) == 0 ){
+                    std::string tmp_url = url_boardbase();
+                    size_t pos = tmp_url.find("://");
+                    if( pos != std::string::npos )
+                        new_url.insert( 0, tmp_url.substr( 0, pos + 1 ) );
+                }
                 int ret = Gtk::RESPONSE_YES;
 
                 if( CONFIG::get_show_movediag() ){
@@ -1187,7 +1207,7 @@
 
                         // 再読み込み
                         const std::string str_tab = "false";
-                        CORE::core_set_command( "open_board", url_subject(), str_tab );
+                        CORE::core_set_command( "open_board", url_boardbase(), str_tab );
                     }
                 }
             }
@@ -1280,17 +1300,20 @@
 
     // 一度全てのarticleをdat落ち状態にして subject.txt に
     // 含まれているものだけ regist_article()の中で通常状態にする
-    if( m_is_online
-        || m_is_booting // ブート中の時も状態を変えないと起動直後にスレ一覧を復元した時にdat落ちしたスレが表示されない
-        ){  
-
-        ArticleHashIterator it = m_hash_article->begin();
-        for( ; it != m_hash_article->end(); ++it ){
+    ArticleHashIterator hash_it = m_hash_article->begin();
+    for( ; hash_it != m_hash_article->end(); ++hash_it ){
 
-            int status = ( *it )->get_status();
+        if( read_from_cache && ! ( *hash_it )->is_924()
+                && ( *hash_it )->get_since_time() > m_last_access_time ){
+            // キャッシュから読み込む場合にsubject.txtよりも新しいスレは残す
+            ( *hash_it )->read_info();
+            if( ! is_abone_thread( *hash_it ) ) m_list_subject.push_back( *hash_it );
+        }
+        else{
+            int status = ( *hash_it )->get_status();
             status &= ~STATUS_NORMAL;
             status |= STATUS_OLD;
-            ( *it )->set_status( status );
+            ( *hash_it )->set_status( status );
         }
     }
 
@@ -1524,7 +1547,8 @@
     if( ! article ) return false;
     if( article->empty() ) return false;
 
-    const int check_number = article->get_number_load() ? 0: ( m_abone_number_thread ? m_abone_number_thread : get_abone_number_global() );
+    const int check_min_number = article->get_number_load() ? 0: ( m_abone_min_number_thread ? m_abone_min_number_thread : get_abone_min_number_global() );
+    const int check_max_number = article->get_number_load() ? 0: ( m_abone_max_number_thread ? m_abone_max_number_thread : get_abone_max_number_global() );
     const int check_hour = article->get_number_load() ? 0: ( m_abone_hour_thread ? m_abone_hour_thread : CONFIG::get_abone_hour_thread() );
     const bool check_thread = ! m_list_abone_thread.empty();
     const bool check_word = ! m_list_abone_word_thread.empty();
@@ -1532,7 +1556,7 @@
     const bool check_word_global = ! CONFIG::get_list_abone_word_thread().empty();
     const bool check_regex_global = ! CONFIG::get_list_abone_regex_thread().empty();
 
-    if( !check_number && !check_hour && !check_thread && !check_word && !check_regex && !check_word_global && !check_regex_global ) return false;
+    if( !check_min_number && !check_max_number && !check_hour && !check_thread && !check_word && !check_regex && !check_word_global && !check_regex_global ) return false;
 
     JDLIB::Regex regex;
     const size_t offset = 0;
@@ -1542,7 +1566,8 @@
     const bool wchar = CONFIG::get_abone_wchar();
 
     // レスの数であぼーん
-    if( check_number ) if( article->get_number() >= check_number ) return true;
+    if( check_min_number ) if( article->get_number() <= check_min_number ) return true;
+    if( check_max_number ) if( article->get_number() >= check_max_number ) return true;
 
     // スレ立てからの時間であぼーん
     if( check_hour ) if( article->get_hour() >= check_hour ) return true;
@@ -1551,7 +1576,7 @@
     if( check_thread ){
         std::list< std::string >::iterator it = m_list_abone_thread.begin();
         for( ; it != m_list_abone_thread.end(); ++it ){
-            if( MISC::remove_space( article->get_subject() ) == MISC::remove_space(*it) ){
+            if( article->get_subject() == *it ){
 
                 // 対象スレがDat落ちした場合はあぼーんしなかったスレ名をリストから消去する
                 // remove_old_abone_thread() も参照
@@ -1676,7 +1701,7 @@
     const bool online = SESSION::is_online();
     SESSION::set_online( false );
 
-    download_subject( ( redraw ? url_subject() : std::string() ), false );
+    download_subject( ( redraw ? url_boardbase() : std::string() ), false );
 
     SESSION::set_online( online );
 }
@@ -1757,7 +1782,8 @@
 void BoardBase::reset_abone_thread( const std::list< std::string >& threads,
                                     const std::list< std::string >& words,
                                     const std::list< std::string >& regexs,
-                                    const int number,
+                                    const int min_number,
+                                    const int max_number,
                                     const int hour,
                                     const bool redraw
     )
@@ -1779,7 +1805,8 @@
     m_list_abone_regex_thread = MISC::remove_space_from_list( regexs );
     m_list_abone_regex_thread = MISC::remove_nullline_from_list( m_list_abone_regex_thread );
 
-    m_abone_number_thread = number;
+    m_abone_min_number_thread = min_number;
+    m_abone_max_number_thread = max_number;
     m_abone_hour_thread = hour;
 
     update_abone_thread( redraw );
@@ -1851,7 +1878,7 @@
     )
 {
 #ifdef _DEBUG
-    std::cout << "BoardBase::search_cache " << url_subject() << std::endl;
+    std::cout << "BoardBase::search_cache " << url_boardbase() << std::endl;
 #endif
 
     if( empty() ) return;
@@ -1861,7 +1888,7 @@
     if( m_hash_article->size() == 0 ) return;
 
     const bool append_all = query.empty();
-    const std::string query_local = MISC::Iconv( query, "UTF-8", get_charset() );
+    const std::string query_local = MISC::Iconv( query, CHARCODE_UTF8, get_charcode() );
     const std::list< std::string > list_query = MISC::split_line( query_local );
 
     const std::string path_board_root = CACHE::path_board_root_fast( url_boardbase() );
@@ -2005,6 +2032,9 @@
 
     m_show_oldlog = cf.get_option_bool( "show_oldlog", false );
 
+    std::string charset = cf.get_option_str( "charset", MISC::charcode_to_cstr( get_charcode() ) );
+    set_charcode( MISC::charcode_from_cstr( charset.c_str() ) );
+
     std::string str_tmp;
 
     // あぼーん id は再起動ごとにリセット
@@ -2036,7 +2066,8 @@
     if( ! str_tmp.empty() ) m_list_abone_regex_thread = MISC::strtolist( str_tmp );
 
     // レス数であぼーん
-    m_abone_number_thread = cf.get_option_int( "abonenumberthread", 0, 0, 9999 );
+    m_abone_min_number_thread = cf.get_option_int( "aboneminnumberthread", 0, 0, MAX_RESNUMBER );
+    m_abone_max_number_thread = cf.get_option_int( "abonenumberthread", 0, 0, MAX_RESNUMBER );
 
     // スレ立てからの経過時間であぼーん
     m_abone_hour_thread = cf.get_option_int( "abonehourthread", 0, 0, 9999 );
@@ -2153,6 +2184,7 @@
          << "view_sort_pre_mode = " << m_view_sort_pre_mode << std::endl
          << "check_noname = " << m_check_noname << std::endl
          << "show_oldlog = " << m_show_oldlog << std::endl
+         << "charset = " << MISC::charcode_to_cstr( get_charcode() ) << std::endl
 
     // IDは再起動ごとにリセット
 //         << "aboneid = " << str_abone_id << std::endl
@@ -2163,7 +2195,8 @@
          << "abonethread = " << str_abone_thread << std::endl
          << "abonewordthread = " << str_abone_word_thread << std::endl
          << "aboneregexthread = " << str_abone_regex_thread << std::endl
-         << "abonenumberthread = " << m_abone_number_thread << std::endl
+         << "aboneminnumberthread = " << m_abone_min_number_thread << std::endl
+         << "abonenumberthread = " << m_abone_max_number_thread << std::endl
          << "abonehourthread = " << m_abone_hour_thread << std::endl
          << "mode_local_proxy = " << m_mode_local_proxy << std::endl
 
@@ -2311,7 +2344,7 @@
             m_status |= STATUS_UPDATE;
 
             // スレ一覧のタブのアイコン表示を更新
-            CORE::core_set_command( "toggle_board_icon", url_subject() );
+            CORE::core_set_command( "toggle_board_icon", url_boardbase() );
 
             // サイドバーのアイコン表示を更新
             CORE::core_set_command( "toggle_sidebar_boardicon", url_datbase() );
@@ -2331,7 +2364,7 @@
 
             // サイドバーのアイコン表示を戻す
             // スレ一覧のタブのアイコンはBoardViewがロード終了時に自動的に戻す
-            CORE::core_set_command( "toggle_sidebar_boardicon", url_subject() );
+            CORE::core_set_command( "toggle_sidebar_boardicon", url_boardbase() );
 
             save_info();
         }
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardbase.h jd-2.8.9-a171004/src/dbtree/boardbase.h
--- jd-2.8.9-150226/src/dbtree/boardbase.h	2014-03-29 15:04:35.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardbase.h	2016-05-11 03:03:53.430102000 +0900
@@ -6,6 +6,7 @@
 #ifndef _BOARDBASE_H
 #define _BOARDBASE_H
 
+#include "jdlib/jdregex.h"
 #include "skeleton/loadable.h"
 
 #include <string>
@@ -107,7 +108,6 @@
         // m_subjecttxt = "subject.txt"
         // m_ext = ".dat"
         // m_id = "hogeboard"
-        // m_charset = "MS932"
         //
         // 先頭に'/'を付けて最後に '/' は付けないことにフォーマットを統一
         //
@@ -120,13 +120,12 @@
         std::string m_subjecttxt;
         std::string m_ext;
         std::string m_id;
-        std::string m_charset;
         std::string m_name; // 板名
 
         // dat型のurlに変換する時のquery ( url_dat()で使用する )
-        std::string m_query_dat;
-        std::string m_query_cgi;
-        std::string m_query_kako;
+        JDLIB::RegexPattern m_query_dat;
+        JDLIB::RegexPattern m_query_cgi;
+        JDLIB::RegexPattern m_query_kako;
 
         // ローカルあぼーん情報(板内の全レス対象)
         std::list< std::string > m_list_abone_id; // あぼーんするID
@@ -139,7 +138,8 @@
         std::list< std::string > m_list_abone_thread_remove; // あぼーんするスレのタイトル( dat 落ち判定用、remove_old_abone_thread()を参照せよ )
         std::list< std::string > m_list_abone_word_thread; // あぼーんする文字列
         std::list< std::string > m_list_abone_regex_thread; // あぼーんする正規表現
-        int m_abone_number_thread; // レスの数
+        int m_abone_min_number_thread; // レスの数(最小)
+        int m_abone_max_number_thread; // レスの数(最大)
         int m_abone_hour_thread; // スレ立てからの経過時間
 
         // 読み込み用ローカルプロキシ設定
@@ -177,9 +177,9 @@
         std::list< std::string > m_url_update_views; // CORE::core_set_command( "update_board" ) を送信するビューのアドレス
         JDLIB::Iconv* m_iconv;
         char* m_rawdata;
-        char* m_rawdata_left;
         size_t m_lng_rawdata;
         size_t m_lng_rawdata_left;
+        CharCode m_charcode_bak;
 
         // 情報ファイルを読みこんだらtrueにして2度読みしないようにする
         bool m_read_info;
@@ -228,7 +228,6 @@
         void set_subjecttxt( const std::string& str ){ m_subjecttxt = str; }
         void set_ext( const std::string& str ){ m_ext = str; }
         void set_id( const std::string& str ){ m_id = str; }
-        void set_charset( const std::string& str ){ m_charset = str; }
 
         // articleがスレあぼーんされているか
         const bool is_abone_thread( ArticleBase* article );
@@ -243,6 +242,10 @@
         // クッキー:HAPの更新 (クッキーをセットした時に実行)
         virtual void update_hap(){}
 
+        // subject.txt の URLを取得
+        // (例) "http://www.hoge2ch.net/hogeboard/subject.txt"
+        const std::string url_subject();
+
       public:
 
         BoardBase( const std::string& root, const std::string& path_board, const std::string& name );
@@ -292,7 +295,6 @@
         const std::string& get_path_board() const { return m_path_board; }
         const std::string& get_ext() const { return m_ext; }
         const std::string& get_id() const { return m_id; }
-        const std::string& get_charset() const { return m_charset; }
         const std::string& get_name() const { return m_name; }
         void update_name( const std::string& name );
         const std::string& get_subjecttxt() const { return m_subjecttxt; }
@@ -365,9 +367,9 @@
         // "http://www.hoge2ch.net/test/read.cgi/hogeboard/12345/12-15"
         virtual const std::string url_readcgi( const std::string& url, int num_from, int num_to );
 
-        // subject.txt の URLを取得
-        // (例) "http://www.hoge2ch.net/hogeboard/subject.txt"
-        const std::string url_subject();
+        // SETTING.TXT の URLを取得
+        // (例) "http://www.hoge2ch.net/hogeboard/SETTING.TXT"
+        virtual const std::string url_settingtxt(){ return std::string(); }
 
         // ルートアドレス
         // (例) "http://www.hoge2ch.net/hogeboard/" なら "http://www.hoge2ch.net/"
@@ -447,7 +449,8 @@
         const std::list< std::string >& get_abone_list_thread_remove(){ return m_list_abone_thread_remove; }
         const std::list< std::string >& get_abone_list_word_thread(){ return m_list_abone_word_thread; }
         const std::list< std::string >& get_abone_list_regex_thread(){ return m_list_abone_regex_thread; }
-        const int get_abone_number_thread(){ return m_abone_number_thread; }
+        const int get_abone_min_number_thread(){ return m_abone_min_number_thread; }
+        const int get_abone_max_number_thread(){ return m_abone_max_number_thread; }
         const int get_abone_hour_thread(){ return m_abone_hour_thread; }
 
         // subject.txtのロード後にdat落ちしたスレッドをスレあぼーんのリストから取り除く
@@ -463,7 +466,8 @@
         void reset_abone_thread( const std::list< std::string >& threads,
                                  const std::list< std::string >& words,
                                  const std::list< std::string >& regexs,
-                                 const int number,
+                                 const int min_number,
+                                 const int max_number,
                                  const int hour,
                                  const bool redraw
             );
@@ -556,6 +560,9 @@
         // キャッシュが存在し、かつdat落ちしていないで新着数が0のスレを速度の順でソートして返す
         const std::list< std::string > get_check_update_articles();
 
+        // 特殊な2chサーバか
+        virtual const bool is_dedicated_server() const{ return false; }
+
       private:
 
         void clear();
@@ -592,7 +599,8 @@
 
         // レス数であぼーん(グローバル)
         // 2ch以外の板ではキャンセルする
-        virtual const int get_abone_number_global(){ return 0; }
+        virtual const int get_abone_min_number_global(){ return 0; }
+        virtual const int get_abone_max_number_global(){ return 0; }
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardjbbs.cpp jd-2.8.9-a171004/src/dbtree/boardjbbs.cpp
--- jd-2.8.9-150226/src/dbtree/boardjbbs.cpp	2013-12-29 20:19:15.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardjbbs.cpp	2017-03-28 19:33:56.148034000 +0900
@@ -6,11 +6,14 @@
 #include "boardjbbs.h"
 #include "articlejbbs.h"
 #include "articlehash.h"
+#include "settingloader.h"
+#include "ruleloader.h"
 
 #include "jdlib/miscutil.h"
 #include "jdlib/miscmsg.h"
 #include "jdlib/loaderdata.h"
 
+#include "httpcode.h"
 #include "global.h"
 
 #include <sstream>
@@ -21,6 +24,8 @@
 
 BoardJBBS::BoardJBBS( const std::string& root, const std::string& path_board, const std::string& name )
     : BoardBase( root, path_board, name )
+    , m_settingloader( NULL )
+    , m_ruleloader( NULL )
 {
     // dat のURLは特殊なので url_datpath()をオーバライドする
     set_path_dat( "" );
@@ -31,7 +36,7 @@
     set_subjecttxt( "subject.txt" );
     set_ext( "" );
     set_id( path_board.substr( 1 ) ); // 先頭の '/' を除く  
-    set_charset( "EUCJP-WIN" );
+    set_charcode( CHARCODE_EUCJP );
 }
 
 
@@ -62,12 +67,9 @@
 {
     if( empty() ) return get_article_null();
 
-    ArticleBase* article = new DBTREE::ArticleJBBS( datbase, id, cached );
+    ArticleBase* article = new DBTREE::ArticleJBBS( datbase, id, cached, get_charcode() );
     if( article ){
         get_hash_article()->push( article );
-
-        // 最大レス数セット
-        article->set_number_max( get_number_max_res() );
     }
     else return get_article_null();
 
@@ -104,11 +106,11 @@
 
     std::stringstream ss_post;
     ss_post.clear();
-    ss_post << "SUBJECT="  << MISC::charset_url_encode( subject, get_charset() )
-            << "&submit="  << MISC::charset_url_encode( "新規書き込み", get_charset() )
-            << "&NAME="    << MISC::charset_url_encode( name, get_charset() )
-            << "&MAIL="    << MISC::charset_url_encode( mail, get_charset() )
-            << "&MESSAGE=" << MISC::charset_url_encode( msg, get_charset() )
+    ss_post << "SUBJECT="  << MISC::url_encode( subject, get_charcode() )
+            << "&submit="  << MISC::url_encode( std::string( "新規書き込み" ), get_charcode() )
+            << "&NAME="    << MISC::url_encode( name, get_charcode() )
+            << "&MAIL="    << MISC::url_encode( mail, get_charcode() )
+            << "&MESSAGE=" << MISC::url_encode( msg, get_charcode() )
             << "&DIR="     << dir
             << "&BBS="     << bbs
             << "&TIME="    << get_time_modified();
@@ -145,13 +147,103 @@
 
 
 
+const std::string BoardJBBS::localrule()
+{
+    if( m_ruleloader ){
+        if( m_ruleloader->is_loading() ) return "ロード中です";
+        else if( m_ruleloader->get_code() == HTTP_OK || m_ruleloader->get_code() == HTTP_REDIRECT || m_ruleloader->get_code() == HTTP_MOVED_PERM ){
+            if( m_ruleloader->get_data().empty() ) return "ローカルルールはありません";
+            else return m_ruleloader->get_data();
+        }
+        else return "ロードに失敗しました : " + m_ruleloader->get_str_code();
+    }
+
+    return BoardBase::localrule();
+}
+
+
+
+//
+// SETTING.TXT のURL
+//
+// (例) "http://jbbs.shitaraba.net/bbs/api/setting.cgi/computer/123/"
+//
+const std::string BoardJBBS::url_settingtxt()
+{
+    return get_root() + "/bbs/api/setting.cgi/" + get_id() + "/";
+}
+
+
+const std::string BoardJBBS::settingtxt()
+{
+    if( m_settingloader ){
+        if( m_settingloader->is_loading() ) return "ロード中です";
+        else if( m_settingloader->get_code() == HTTP_OK || m_settingloader->get_code() == HTTP_REDIRECT || m_settingloader->get_code() == HTTP_MOVED_PERM ){
+            if( m_settingloader->get_data().empty() ) return "SETTING.TXTはありません";
+            else return m_settingloader->get_data();
+        }
+        else return "ロードに失敗しました : " + m_settingloader->get_str_code();
+    }
+
+    return BoardBase::settingtxt();
+}
+
+
+const std::string BoardJBBS::default_noname()
+{
+    if( m_settingloader
+        && m_settingloader->get_code() == HTTP_OK ) return m_settingloader->default_noname();
+
+    return BoardBase::default_noname();
+}
+
+
+//
+// ローカルルールとSETTING.TXTをキャッシュから読み込む
+//
+// BoardBase::read_info()で呼び出す
+//
+void BoardJBBS::load_rule_setting()
+{
+#ifdef _DEBUG
+    std::cout << "BoardJBBS::load_rule_setting" << std::endl;
+#endif
+
+    if( ! m_ruleloader ) m_ruleloader = new RuleLoader( url_boardbase() );
+    m_ruleloader->load_text( CHARCODE_SJIS );
+
+    if( ! m_settingloader ) m_settingloader = new SettingLoader( url_boardbase() );
+    m_settingloader->load_text( get_charcode() );
+}
+
+
+//
+// SETTING.TXTをサーバからダウンロード(ローカルルールはダウンロードしない)
+//
+// 読み込むタイミングはsubject.txtを読み終わった直後( BoardBase::receive_finish() )
+//
+void BoardJBBS::download_rule_setting()
+{
+#ifdef _DEBUG
+    std::cout << "BoardJBBS::download_rule_setting" << std::endl;
+#endif
+
+    if( ! m_ruleloader ) m_ruleloader = new RuleLoader( url_boardbase() );
+    m_ruleloader->download_text( CHARCODE_SJIS );
+
+    if( ! m_settingloader ) m_settingloader = new SettingLoader( url_boardbase() );
+    m_settingloader->download_text( get_charcode() );
+}
+
+
+
 //
 // subject.txt から Aarticle のリストにアイテムを追加・更新
 //
 void BoardJBBS::parse_subject( const char* str_subject_txt )
 {
 #ifdef _DEBUG
-    std::cout << "BoardJBBS::parse_subject\n";
+    std::cout << "BoardJBBS::parse_subject" << std::endl;
 #endif 
    
     const char* pos = str_subject_txt;
@@ -210,9 +302,6 @@
         artinfo.id = MISC::remove_space( artinfo.id );
 
         artinfo.subject.assign( str_subject, lng_subject );
-        artinfo.subject = MISC::remove_space( artinfo.subject );
-        artinfo.subject = MISC::replace_str( artinfo.subject, "&lt;", "<" );
-        artinfo.subject = MISC::replace_str( artinfo.subject, "&gt;", ">" );
         
         artinfo.number = atol( str_num );
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardjbbs.h jd-2.8.9-a171004/src/dbtree/boardjbbs.h
--- jd-2.8.9-150226/src/dbtree/boardjbbs.h	2010-04-02 01:35:28.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardjbbs.h	2015-06-29 17:28:16.290215000 +0900
@@ -11,8 +11,14 @@
 
 namespace DBTREE
 {
+    class SettingLoader;
+    class RuleLoader;
+
     class BoardJBBS : public BoardBase
     {
+        SettingLoader* m_settingloader;
+        RuleLoader* m_ruleloader;
+
       public:
 
         BoardJBBS( const std::string& root, const std::string& path_board,const std::string& name );
@@ -28,12 +34,24 @@
         // 新スレ作成用のsubbbscgi のURL
         virtual const std::string url_subbbscgi_new();
 
+        // ローカルルール
+        virtual const std::string localrule();
+
+        // SETTING.TXT のURL
+        virtual const std::string url_settingtxt();
+        // SETTING.TXT 
+        virtual const std::string settingtxt();
+        virtual const std::string default_noname();
+
       private:
 
         virtual const bool is_valid( const std::string& filename );
         virtual ArticleBase* append_article( const std::string& datbase, const std::string& id, const bool cached );
         virtual void parse_subject( const char* str_subject_txt );
         virtual void regist_article( const bool is_online );
+
+        virtual void load_rule_setting();
+        virtual void download_rule_setting();
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardlocal.cpp jd-2.8.9-a171004/src/dbtree/boardlocal.cpp
--- jd-2.8.9-150226/src/dbtree/boardlocal.cpp	2010-06-16 00:08:08.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardlocal.cpp	2016-01-02 18:49:43.934166000 +0900
@@ -71,7 +71,13 @@
 }
 
 
-void BoardLocal::download_subject( const std::string& url_update_view )
+const std::string BoardLocal::url_datpath()
+{
+    return std::string();
+}
+
+
+void BoardLocal::download_subject( const std::string& url_update_view, const bool read_from_cache )
 {
     // ダウンロードを実行しない
     get_url_update_views().push_back( url_update_view );
@@ -86,7 +92,7 @@
               << ", id = " << id << std::endl;
 #endif
 
-    ArticleBase* article = new DBTREE::ArticleLocal( datbase, id );
+    ArticleBase* article = new DBTREE::ArticleLocal( datbase, id, get_charcode() );
     if( article ){
 
         get_hash_article()->push( article );
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardlocal.h jd-2.8.9-a171004/src/dbtree/boardlocal.h
--- jd-2.8.9-150226/src/dbtree/boardlocal.h	2009-10-31 23:28:56.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardlocal.h	2016-01-02 18:48:40.834167000 +0900
@@ -23,15 +23,16 @@
 
         virtual const std::string url_dat( const std::string& url, int& num_from, int& num_to, std::string& num_str ); 
         virtual const std::string url_readcgi( const std::string& url, int num_from, int num_to );
+        virtual const std::string url_datpath();
 
-        virtual void download_subject( const std::string& url_update_view );
+        virtual void download_subject( const std::string& url_update_view, const bool read_from_cache );
 
         // 板情報の読み書きをキャンセル
         virtual void read_info(){}
         virtual void save_info(){}
 
         // キャッシュサーチをキャンセル
-        virtual void search_cache( std::list< ArticleBase* >& list_article, const std::string& query, const bool mode_or, const bool& stop ){}
+        virtual void search_cache( std::vector< ArticleBase* >& list_article, const std::string& query, const bool mode_or, const bool bm, const bool& stop ){}
 
         // datファイルのインポート
         virtual const std::string import_dat( const std::string& filename );
diff -aurNZ jd-2.8.9-150226/src/dbtree/boardmachi.cpp jd-2.8.9-a171004/src/dbtree/boardmachi.cpp
--- jd-2.8.9-150226/src/dbtree/boardmachi.cpp	2013-01-13 00:06:22.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/boardmachi.cpp	2017-03-28 19:33:04.692034000 +0900
@@ -31,7 +31,7 @@
     set_subjecttxt( "subject.txt" );
     set_ext( "" );
     set_id( path_board.substr( 1 ) ); // 先頭の '/' を除く  
-    set_charset( "MS932" );
+    set_charcode( CHARCODE_SJIS );
 }
 
 
@@ -79,12 +79,9 @@
 {
     if( empty() ) return get_article_null();
 
-    ArticleBase* article = new DBTREE::ArticleMachi( datbase, id, cached );
+    ArticleBase* article = new DBTREE::ArticleMachi( datbase, id, cached, get_charcode() );
     if( article ){
         get_hash_article()->push( article );
-
-        // 最大レス数セット
-        article->set_number_max( get_number_max_res() );
     }
     else return get_article_null();
 
@@ -270,9 +267,6 @@
         artinfo.id = MISC::remove_space( artinfo.id );
 
         artinfo.subject.assign( str_subject, lng_subject );
-        artinfo.subject = MISC::remove_space( artinfo.subject );
-        artinfo.subject = MISC::replace_str( artinfo.subject, "&lt;", "<" );
-        artinfo.subject = MISC::replace_str( artinfo.subject, "&gt;", ">" );
         
         artinfo.number = atol( str_num );
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/interface.cpp jd-2.8.9-a171004/src/dbtree/interface.cpp
--- jd-2.8.9-150226/src/dbtree/interface.cpp	2014-03-29 15:04:35.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/interface.cpp	2017-03-28 00:44:51.949072000 +0900
@@ -60,12 +60,6 @@
 
 //////////////////////////////////////
 
-const std::string DBTREE::url_subject( const std::string& url )
-{
-    return DBTREE::get_board( url )->url_subject();
-}
-
-
 const std::string DBTREE::url_root( const std::string& url )
 {
     return DBTREE::get_board( url )->url_root();
@@ -139,6 +133,13 @@
 }
 
 
+const std::string DBTREE::url_settingtxt( const std::string& url )
+{
+    return DBTREE::get_board( url )->url_settingtxt();
+}
+
+
+
 // 簡易版
 const std::string  DBTREE::is_board_moved( const std::string& url )
 {
@@ -231,13 +232,13 @@
 }
 
 
-const std::string DBTREE::board_path( const std::string& url )
+const std::string& DBTREE::board_path( const std::string& url )
 {
     return DBTREE::get_board( url )->get_path_board();
 }
 
 
-const std::string DBTREE::board_id( const std::string& url )
+const std::string& DBTREE::board_id( const std::string& url )
 {
     return DBTREE::get_board( url )->get_id();
 }
@@ -249,7 +250,7 @@
 }
 
 // 板の更新時間( 文字列 )
-const std::string DBTREE::board_date_modified( const std::string& url )
+const std::string& DBTREE::board_date_modified( const std::string& url )
 {
     return DBTREE::get_board( url )->get_date_modified();
 }
@@ -281,21 +282,27 @@
 }
 
 
-const std::string DBTREE::board_name( const std::string& url )
+const std::string& DBTREE::board_name( const std::string& url )
 {
     return DBTREE::get_board( url )->get_name();
 }
 
 
-const std::string DBTREE::board_subjecttxt( const std::string& url )
+const std::string& DBTREE::board_subjecttxt( const std::string& url )
 {
     return DBTREE::get_board( url )->get_subjecttxt();
 }
 
 
-const std::string DBTREE::board_charset( const std::string& url )
+const CharCode DBTREE::board_charcode( const std::string& url )
 {
-    return DBTREE::get_board( url )->get_charset();
+    return DBTREE::get_board( url )->get_charcode();
+}
+
+
+void DBTREE::board_set_charcode( const std::string& url, const CharCode charcode )
+{
+    return DBTREE::get_board( url )->set_charcode( charcode );
 }
 
 
@@ -321,7 +328,7 @@
     DBTREE::get_board( url )->reset_list_cookies_for_write();
 }
 
-const std::string DBTREE::board_keyword_for_write( const std::string& url )
+const std::string& DBTREE::board_keyword_for_write( const std::string& url )
 {
     return DBTREE::get_board( url )->get_keyword_for_write();
 }
@@ -339,13 +346,13 @@
 }
 
 
-const std::string DBTREE::board_basicauth( const std::string& url )
+const std::string& DBTREE::board_basicauth( const std::string& url )
 {
     return DBTREE::get_board( url )->get_basicauth();
 }
 
 
-const std::string DBTREE::board_ext( const std::string& url )
+const std::string& DBTREE::board_ext( const std::string& url )
 {
     return DBTREE::get_board( url )->get_ext();
 }
@@ -363,7 +370,7 @@
 }
 
 
-const std::string DBTREE::board_str_code( const std::string& url )
+const std::string& DBTREE::board_str_code( const std::string& url )
 {
     return DBTREE::get_board( url )->get_str_code();
 }
@@ -670,6 +677,13 @@
 }
 
 
+// 特殊な2chサーバか
+const bool DBTREE::board_is_dedicated_server( const std::string& url )
+{
+    return DBTREE::get_board( url )->is_dedicated_server();
+}
+
+
 /////////////////////////////////////////////////
 
 
@@ -742,9 +756,14 @@
     return DBTREE::get_board( url )->get_abone_list_regex_thread();
 }
 
-const int DBTREE::get_abone_number_thread( const std::string& url )
+const int DBTREE::get_abone_min_number_thread( const std::string& url )
 {
-    return DBTREE::get_board( url )->get_abone_number_thread();
+    return DBTREE::get_board( url )->get_abone_min_number_thread();
+}
+
+const int DBTREE::get_abone_max_number_thread( const std::string& url )
+{
+    return DBTREE::get_board( url )->get_abone_max_number_thread();
 }
 
 const int DBTREE::get_abone_hour_thread( const std::string& url )
@@ -767,12 +786,13 @@
                                  const std::list< std::string >& threads,
                                  const std::list< std::string >& words,
                                  const std::list< std::string >& regexs,
-                                 const int number,
+                                 const int min_number,
+                                 const int max_number,
                                  const int hour,
                                  const bool redraw
     )
 {
-    DBTREE::get_board( url )->reset_abone_thread( threads, words, regexs, number, hour, redraw );
+    DBTREE::get_board( url )->reset_abone_thread( threads, words, regexs, min_number, max_number, hour, redraw );
 }
 
 /////////////////////////////////////////////////
@@ -836,6 +856,18 @@
     DBTREE::get_article( url )->set_date_modified( date );
 }
 
+// スレの文字コード
+const CharCode DBTREE::article_charcode( const std::string& url )
+{
+    return DBTREE::get_article( url )->get_charcode();
+}
+
+// スレの文字コードをセット
+void DBTREE::article_set_charcode( const std::string& url, const CharCode charcode )
+{
+    DBTREE::get_article( url )->set_charcode( charcode );
+}
+
 const int  DBTREE::article_hour( const std::string& url )
 {
     return DBTREE::get_article( url )->get_hour();
@@ -876,11 +908,15 @@
     return DBTREE::get_article( url )->get_ext_err();
 }
 
-const std::string DBTREE::article_subject( const std::string& url )
+const std::string& DBTREE::article_subject( const std::string& url )
 {
     return DBTREE::get_article( url )->get_subject();
 }
 
+const std::string& DBTREE::article_modified_subject( const std::string& url, const bool renew )
+{
+    return DBTREE::get_article( url )->get_modified_subject( renew );
+}
 
 const int DBTREE::article_number( const std::string& url )
 {
@@ -907,6 +943,16 @@
     return DBTREE::get_article( url )->get_number_new();
 }
 
+const int DBTREE::article_number_max( const std::string& url )
+{
+    return DBTREE::get_article( url )->get_number_max();
+}
+
+void DBTREE::article_set_number_max( const std::string& url, int max )
+{
+    DBTREE::get_article( url )->set_number_max( max );
+}
+
 const bool DBTREE::article_is_loading( const std::string& url )
 {
     return DBTREE::get_article( url )->is_loading();
@@ -951,6 +997,13 @@
 }
 
 
+// NodeTree削除
+void DBTREE::article_clear_nodetree( const std::string& url )
+{
+    DBTREE::get_article( url )->unlock_impl();
+}
+
+
 // ユーザーエージェント
 // ダウンロード用
 const std::string& DBTREE::get_agent( const std::string& url )
@@ -1182,10 +1235,11 @@
                           const std::list< std::string >& regexs,
                           const std::vector< char >& vec_abone_res,
                           const bool transparent, const bool chain, const bool age,
+                          const bool default_name, const bool noid,
                           const bool board, const bool global
     )
 {
-    DBTREE::get_article( url )->reset_abone( ids, names, words, regexs, vec_abone_res, transparent, chain, age, board, global );
+    DBTREE::get_article( url )->reset_abone( ids, names, words, regexs, vec_abone_res, transparent, chain, age, default_name, noid, board, global );
 }
 
 
@@ -1251,6 +1305,32 @@
 }
 
 
+// デフォルト名無しあぼーん
+const bool DBTREE::get_abone_default_name( const std::string& url )
+{
+    return DBTREE::get_article( url )->get_abone_default_name();
+}
+
+
+void DBTREE::set_abone_default_name( const std::string& url, const bool set )
+{
+    DBTREE::get_article( url )->set_abone_default_name( set );
+}
+
+
+// ID無しあぼーん
+const bool DBTREE::get_abone_noid( const std::string& url )
+{
+    return DBTREE::get_article( url )->get_abone_noid();
+}
+
+
+void DBTREE::set_abone_noid( const std::string& url, const bool set )
+{
+    DBTREE::get_article( url )->set_abone_noid( set );
+}
+
+
 // 板レベルでのあぼーん
 const bool DBTREE::get_abone_board( const std::string& url )
 {
diff -aurNZ jd-2.8.9-150226/src/dbtree/interface.h jd-2.8.9-a171004/src/dbtree/interface.h
--- jd-2.8.9-150226/src/dbtree/interface.h	2014-03-29 15:04:35.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/interface.h	2017-03-28 00:44:54.697072000 +0900
@@ -7,6 +7,7 @@
 #ifndef _INTERFACE_H
 #define _INTERFACE_H
 
+#include "charcode.h"
 #include "etcboardinfo.h"
 
 #include <string>
@@ -36,7 +37,6 @@
     ArticleBase* get_article( const std::string& url );
 
     // urlの変換関係
-    const std::string url_subject( const std::string& url ); // 板の subject.txt の URL
     const std::string url_root( const std::string& url );
     const std::string url_boardbase( const std::string& url );
     const std::string url_datbase( const std::string& url );
@@ -57,6 +57,8 @@
     const std::string url_bbscgi_new( const std::string& url );
     const std::string url_subbbscgi_new( const std::string& url );
 
+    const std::string url_settingtxt( const std::string& url );
+
     // 板が移転したかチェックする
     // 移転した時は移転後のURLを返す
     const std::string is_board_moved( const std::string& url );
@@ -91,30 +93,31 @@
     const time_t get_time_modified(); // bbsmenuの更新時間( time_t )
     
     // board 系
-    const std::string board_path( const std::string& url );
-    const std::string board_id( const std::string& url );
+    const std::string& board_path( const std::string& url );
+    const std::string& board_id( const std::string& url );
     const time_t board_time_modified( const std::string& url ); // 板の更新時間( time_t )
-    const std::string board_date_modified( const std::string& url ); // 板の更新時間( 文字列 )
+    const std::string& board_date_modified( const std::string& url ); // 板の更新時間( 文字列 )
     void board_set_date_modified( const std::string& url, const std::string& date ); // 板の更新時間( 文字列 )をセット
     const std::string& board_get_modified_localrule( const std::string& url );
     void board_set_modified_localrule( const std::string& url, const std::string& modified );
     const std::string& board_get_modified_setting( const std::string& url );
     void board_set_modified_setting( const std::string& url, const std::string& modified );
-    const std::string board_name( const std::string& url );
-    const std::string board_subjecttxt( const std::string& url );
-    const std::string board_charset( const std::string& url );
+    const std::string& board_name( const std::string& url );
+    const std::string& board_subjecttxt( const std::string& url );
+    const CharCode board_charcode( const std::string& url );
+    void board_set_charcode( const std::string& url, const CharCode charcode );
     const std::string board_cookie_for_write( const std::string& url );
     const std::list< std::string >& board_list_cookies_for_write( const std::string& url );
     void board_set_list_cookies_for_write( const std::string& url, const std::list< std::string>& list_cookies );        
     void board_reset_list_cookies_for_write( const std::string& url );
-    const std::string board_keyword_for_write( const std::string& url );
+    const std::string& board_keyword_for_write( const std::string& url );
     void board_set_keyword_for_write( const std::string& url, const std::string& keyword );
     void board_analyze_keyword_for_write( const std::string& url, const std::string& html );
-    const std::string board_basicauth( const std::string& url );
-    const std::string board_ext( const std::string& url );
+    const std::string& board_basicauth( const std::string& url );
+    const std::string& board_ext( const std::string& url );
     const int board_status( const std::string& url );
     const int board_code( const std::string& url );
-    const std::string board_str_code( const std::string& url );
+    const std::string& board_str_code( const std::string& url );
     void board_save_info( const std::string& url );
     void board_download_subject( const std::string& url, const std::string& url_update_view );
     void board_read_subject_from_cache( const std::string& url );
@@ -183,6 +186,9 @@
     void board_set_live_sec( const std::string& url, time_t sec );
     const time_t board_last_access_time( const std::string& url );
 
+    // 特殊な2chサーバか
+    const bool board_is_dedicated_server( const std::string& url );
+
     // 全スレの書き込み履歴のリセット
     void clear_all_post_history();
 
@@ -208,6 +214,8 @@
     const time_t article_time_modified( const std::string& url ); // スレの更新時間( time_t )
     const std::string article_date_modified( const std::string& url ); // スレの更新時間( 文字列 )
     void article_set_date_modified( const std::string& url, const std::string& date ); // スレの更新時間( 文字列 )をセット
+    const CharCode article_charcode( const std::string& url );
+    void article_set_charcode( const std::string& url, const CharCode charcode );
     const int article_hour( const std::string& url );
     const time_t article_write_time( const std::string& url );
     const std::string article_write_date( const std::string& url );
@@ -215,12 +223,15 @@
     const int article_code( const std::string& url );
     const std::string article_str_code( const std::string& url );
     const std::string article_ext_err( const std::string& url );
-    const std::string article_subject( const std::string& url );
+    const std::string& article_subject( const std::string& url );
+    const std::string& article_modified_subject( const std::string& url, const bool renew = false );
     const int article_number( const std::string& url );    
     const int article_number_load( const std::string& url );
     const int article_number_seen( const std::string& url );
     void article_set_number_seen( const std::string& url, int seen );
     const int article_number_new( const std::string& url );    
+    const int article_number_max( const std::string& url );
+    void article_set_number_max( const std::string& url, int max );
     const bool article_is_loading( const std::string& url );
     const bool article_is_checking_update( const std::string& url );
     void article_download_dat( const std::string& url, const bool check_update );
@@ -245,6 +256,9 @@
     void article_update_writetime( const std::string& url );
     size_t article_lng_dat( const std::string& url );
 
+    // NodeTree削除
+    void article_clear_nodetree( const std::string& url );
+
     // ユーザーエージェント
     const std::string& get_agent( const std::string& url );   // ダウンロード用
     const std::string& get_agent_w( const std::string& url ); // 書き込み用
@@ -327,7 +341,8 @@
     const std::list< std::string >& get_abone_list_word_thread( const std::string& url );
     const std::list< std::string >& get_abone_list_regex_thread( const std::string& url );
     const std::vector< char >& get_abone_vec_res( const std::string& url );
-    const int get_abone_number_thread( const std::string& url );
+    const int get_abone_min_number_thread( const std::string& url );
+    const int get_abone_max_number_thread( const std::string& url );
     const int get_abone_hour_thread( const std::string& url );
 
     // subject.txtのロード後にdat落ちしたスレッドをスレあぼーんのリストから取り除く
@@ -343,7 +358,8 @@
                              const std::list< std::string >& threads,
                              const std::list< std::string >& words,
                              const std::list< std::string >& regexs,
-                             const int number, const int hour, const bool redraw );
+                             const int min_number, const int max_number,
+                             const int hour, const bool redraw );
 
     //
     // 各articlebase別のあぼーん情報
@@ -372,6 +388,7 @@
                       const std::list< std::string >& regexs,
                       const std::vector< char >& vec_abone_res,
                       const bool transparent, const bool chain, const bool age,
+                      const bool default_name, const bool noid,
                       const bool board, const bool global
         );
 
@@ -393,6 +410,14 @@
     const bool get_abone_age( const std::string& url );
     void set_abone_age( const std::string& url, const bool set );
 
+    // デフォルト名無しあぼーん
+    const bool get_abone_default_name( const std::string& url );
+    void set_abone_default_name( const std::string& url, const bool set );
+
+    // ID無しあぼーん
+    const bool get_abone_noid( const std::string& url );
+    void set_abone_noid( const std::string& url, const bool set );
+
     // 板レベルでのあぼーん
     const bool get_abone_board( const std::string& url );
     void set_abone_board( const std::string& url, const bool set );
diff -aurNZ jd-2.8.9-150226/src/dbtree/node.h jd-2.8.9-a171004/src/dbtree/node.h
--- jd-2.8.9-150226/src/dbtree/node.h	2013-03-03 13:30:00.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/node.h	2016-02-11 02:16:07.109638000 +0900
@@ -29,11 +29,9 @@
         NODE_DIV,   // div
         NODE_IMG,   // img
 
-        // スペース(幅0)
-        NODE_ZWSP,
-
-        // 連続半角スペース
-        NODE_MULTISP,
+        NODE_SP,    // スペース
+        NODE_ZWSP,  // スペース(幅0)
+        NODE_MULTISP, // 連続半角スペース
 
         // 水平タブ(0x09)
         NODE_HTAB,
@@ -118,6 +116,7 @@
         
         char* text;
         unsigned char color_text; // 色
+        unsigned char color_back; // 背景色
         bool bold;
         char fontid; // fontid.h
         
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetree2ch.cpp jd-2.8.9-a171004/src/dbtree/nodetree2ch.cpp
--- jd-2.8.9-150226/src/dbtree/nodetree2ch.cpp	2014-04-26 22:27:31.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetree2ch.cpp	2017-10-04 18:30:54.013971222 +0900
@@ -6,9 +6,13 @@
 #include "nodetree2ch.h"
 #include "interface.h"
 
+#include "jdlib/jdiconv.h"
 #include "jdlib/jdregex.h"
 #include "jdlib/loaderdata.h"
-#include "jdlib/miscutil.h"
+#include "jdlib/misccharcode.h"
+#include "jdlib/miscmsg.h"
+#include "jdlib/misctime.h"
+#include "jdlib/misctrip.h"
 
 #include "config/globalconf.h"
 
@@ -16,7 +20,6 @@
 #include "session.h"
 #include "login2ch.h"
 
-#include <sstream>
 
 using namespace DBTREE;
 
@@ -24,10 +27,14 @@
 enum
 {
     MODE_NORMAL = 0,
-    MODE_OFFLAW,
-    MODE_OFFLAW2,
+    MODE_RETRY,
+    MODE_CGI,
+    MODE_JSON,
+    MODE_KAKO_API,
     MODE_KAKO_GZ,
-    MODE_KAKO
+    MODE_KAKO,
+    MODE_KAKO_EXT,
+    MODE_KAKO_CGI
 };
 
 
@@ -37,6 +44,8 @@
     , m_org_url( org_url )
     , m_since_time( since_time )
     , m_mode( MODE_NORMAL )
+    , m_res_number_max( -1 )
+    , m_json_is_string( false )
 {
 #ifdef _DEBUG
     std::cout << "NodeTree2ch::NodeTree2ch url = " << url << std::endl
@@ -60,31 +69,76 @@
 //
 // 先頭にrawモードのステータスが入っていたら取り除く
 //
-char* NodeTree2ch::process_raw_lines( char* rawlines )
+char* NodeTree2ch::process_raw_lines( char* rawlines, size_t& size )
 {
-    char* pos = rawlines;
-
-    if( m_mode == MODE_OFFLAW ){
-        // rokka独自のステータスが入っている
-
-        int status = 0;
-        if( strncmp( pos, "Success", 7 ) == 0 ) status = 1;
-        if( strncmp( pos, "Error", 5 ) == 0 ) status = 2;
 
+    if( ! is_loading() && memcmp( rawlines, "<dt>", 4 ) == 0 ){
 #ifdef _DEBUG
-        std::cout << "NodeTree2ch::process_raw_lines : raw mode status = " << status << std::endl;
+        std::cout << "NodeTree2ch::process_raw_lines ignore HTML lines" << std::endl;
 #endif
-
-        if( status != 0 ){
-            pos = skip_status_line( pos, status );
+        *rawlines = '\0';
+    }
+    else if( ! is_loading() && memcmp( rawlines, "ng (", 4 ) == 0
+            && strstr( rawlines, "<>" ) == NULL ){
+        set_code( HTTP_ERR );
+        set_str_code( rawlines );
+        MISC::ERRMSG( rawlines );
+        *rawlines = '\0';
+    }
+    else if( size == 16 && memcmp( rawlines, "Dat doesnt exist", 16 ) == 0 ){
+        set_ext_err( rawlines );
+        *rawlines = '\0';
+    }
+    else if( m_mode == MODE_CGI || m_mode == MODE_KAKO_CGI ) html2dat( rawlines, size );
+    else if( m_mode == MODE_JSON ) json2dat( rawlines );
+    else if( m_mode == MODE_NORMAL && is_loading() && ! id_header() ){
+        char *l1, *l2;
+        if( ! ( l1 = strchr( rawlines, '\n' ) ) ) return rawlines;
+        if( ! ( l2 = strchr( l1 + 1, '\n' ) ) || *( l2 + 1 ) != '\0' ) return rawlines;
+
+        char *id = strstr( l1 + 1, " ID:\?\?\?\?\?\?\?\?<>" );
+        if( id && id < l2 ){
+            // ログ落ち案内レス
+            m_operate_info = l1 + 1;       // レスをプールする
+            *( l1 + 1 ) = '\0';
         }
     }
 
-    else {
-        pos = NodeTree2chCompati::process_raw_lines( rawlines );
+    return rawlines;
+}
+
+
+//
+// 拡張属性を取り出す
+//
+void NodeTree2ch::parse_extattr( const char* str, const int lng )
+{
+    const char* pos = str;
+
+    while( pos < str + lng && ( pos = (const char *)memchr( pos, '<', str + lng - pos ) ) != NULL ){
+        if( memcmp( pos, "<hr>VIPQ2_EXTDAT: ", 18 ) == 0 ){ pos += 18; break; }
+        ++pos;
     }
 
-    return pos;
+    if( pos != NULL && pos < str + lng ){
+        JDLIB::Regex regex;
+        const size_t offset = 0;
+        const bool icase = false; // 大文字小文字区別しない
+        const bool newline = true; // . に改行をマッチさせない
+        const bool usemigemo = false; // migemo使用
+        const bool wchar = false; // 全角半角の区別をしない
+
+        std::string extattr( pos, str + lng - pos );
+        if( regex.exec( "[^:]+:[^:]+:([^:]+):[^ ]+ EXT was configured",
+                    extattr.c_str(), offset, icase, newline, usemigemo, wchar ) ){
+
+            // 最大レス数を取得
+            if( regex.str( 1 ) == "V" ) m_res_number_max = 0;
+            else if( regex.str( 1 )[ 0 ] >= '0' && regex.str( 1 )[ 0 ] <= '9' ){
+                m_res_number_max = atoi( regex.str( 1 ).c_str() );
+            }
+        }
+    }
 }
 
 
@@ -98,97 +152,106 @@
 
 #endif
 
-    data.url = std::string();
     data.byte_readfrom = 0;
 
-    //rokka使用 (旧offlawは廃止)
-    if( m_mode == MODE_OFFLAW ){
+    JDLIB::Regex regex;
+    const size_t offset = 0;
+    const bool icase = false;
+    const bool newline = true;
+    const bool usemigemo = false;
+    const bool wchar = false;
 
-        JDLIB::Regex regex;
-        const size_t offset = 0;
-        const bool icase = false;
-        const bool newline = true;
-        const bool usemigemo = false;
-        const bool wchar = false;
-
-        if( ! regex.exec( "(http://)([^/\\.]+)(\\.[^/]+)(/.*)/dat(/.*)\\.dat$", m_org_url, offset, icase, newline, usemigemo, wchar ) ) return;
-
-        // http://rokka(.2ch.net|.bbspink.com)/<SERVER NAME>/<BOARD NAME>/<DAT NUMBER>/<OPTIONS>?sid=<SID>
-        std::ostringstream ss;
-        ss << regex.str( 1 ) << "rokka" << regex.str( 3 ) << "/" << regex.str( 2 )
-           << regex.str( 4 ) << regex.str( 5 );
+    // ( rokka, 旧offlaw, offlaw2は廃止 )
+    // 過去ログ倉庫使用
+    if( m_mode == MODE_KAKO_GZ || m_mode == MODE_KAKO ){
+
+        if( ! regex.exec( "(https?://[^/]*)(/.*)/dat(/.*)\\.dat$", m_org_url, offset, icase, newline, usemigemo, wchar ) ) return;
+        const int id = atoi( regex.str( 3 ).c_str() + 1 );
 
-        std::string sid = CORE::get_login2ch()->get_sessionid();
-        ss << "/?sid=" << MISC::url_encode( sid.c_str(), sid.length() );
+        std::string url;
 
-        // レジューム設定
-        // レジュームを有りにして、サーバが range を無視して送ってきた場合と同じ処理をする
-        if( get_lng_dat() ) {
-            set_resume( true );
-        }
-        else set_resume( false );
+        // スレIDが10桁の場合 → http://サーバ/板ID/kako/IDの上位4桁/IDの上位5桁/ID.dat.gz
+        if( id / 1000000000 ) url = regex.str( 1 ) + regex.str( 2 ) + "/kako/" + MISC::itostr( id / 1000000 ) + "/" + MISC::itostr( id / 100000 ) + regex.str( 3 );
 
-        data.url = ss.str();
-    }
+        // スレIDが9桁の場合 → http://サーバ/板ID/kako/IDの上位3桁/ID.dat.gz
+        else url = regex.str( 1 ) + regex.str( 2 ) + "/kako/" + MISC::itostr( id / 1000000 ) + regex.str( 3 );
 
-    //offlaw2 使用
-    else if( m_mode == MODE_OFFLAW2 ){
+        if( m_mode == MODE_KAKO_GZ ) url += ".dat.gz";
+        else url += ".dat";
 
-        JDLIB::Regex regex;
-        const size_t offset = 0;
-        const bool icase = false;
-        const bool newline = true;
-        const bool usemigemo = false;
-        const bool wchar = false;
-
-        if( ! regex.exec( "(http://[^/]*)/(.*)/dat/(.*)\\.dat$", m_org_url, offset, icase, newline, usemigemo, wchar ) ) return;
-
-        std::ostringstream ss;
-        ss << regex.str( 1 ) << "/test/offlaw2.so?shiro=kuma&sid=ERROR&bbs=" << regex.str( 2 )
-           << "&key=" << regex.str( 3 );
+        data.url = url;
 
         // レジューム設定
-        // レジュームを有りにして、サーバが range を無視して送ってきた場合と同じ処理をする
-        if( get_lng_dat() ) {
-            set_resume( true );
-        }
+        if( get_lng_dat() ) set_resume( true );
         else set_resume( false );
-
-        data.url = ss.str();
     }
 
-    // 過去ログ倉庫使用
-    else if( m_mode == MODE_KAKO_GZ || m_mode == MODE_KAKO ){
+    else if( m_mode == MODE_KAKO_EXT ){
+        if( ! regex.exec( "https?://([^./]+)\\.[^/]+/(.*)/dat/(.*)\\.dat$", m_org_url, offset, icase, newline, usemigemo, wchar ) ) return;
 
-        JDLIB::Regex regex;
-        const size_t offset = 0;
-        const bool icase = false;
-        const bool newline = true;
-        const bool usemigemo = false;
-        const bool wchar = false;
+        std::string cmd = CONFIG::get_url_external_log();
+        cmd = MISC::replace_str( cmd, "$OLDHOST", regex.str( 1 ) );
+        cmd = MISC::replace_str( cmd, "$BBSNAME", regex.str( 2 ) );
+        cmd = MISC::replace_str( cmd, "$DATNAME", regex.str( 3 ) );
 
-        if( ! regex.exec( "(http://[^/]*)(/.*)/dat(/.*)\\.dat$", m_org_url, offset, icase, newline, usemigemo, wchar ) ) return;
-        const int id = atoi( regex.str( 3 ).c_str() + 1 );
+        data.url = cmd;
 
-        std::ostringstream ss;
+        // レジューム設定
+        if( get_lng_dat() ) set_resume( true );
+        else set_resume( false );
+    }
 
-        // スレIDが10桁の場合 → http://サーバ/板ID/kako/IDの上位4桁/IDの上位5桁/ID.dat.gz
-        if( id / 1000000000 ) ss << regex.str( 1 ) << regex.str( 2 ) << "/kako/" << ( id / 1000000 ) << "/" << ( id / 100000 ) << regex.str( 3 );
+    // json使用
+    else if( m_mode == MODE_JSON ){
 
-        // スレIDが9桁の場合 → http://サーバ/板ID/kako/IDの上位3桁/ID.dat.gz
-        else ss << regex.str( 1 ) << regex.str( 2 ) << "/kako/" << ( id / 1000000 ) << regex.str( 3 );
+        if( ! regex.exec( "https?://([^.]+)[^/]*/([^/]+)/dat/([0-9]+)\\.dat$", get_url(), offset, icase, newline, usemigemo, wchar ) ) return;
 
-        if( m_mode == MODE_KAKO_GZ ) ss << ".dat.gz";
-        else ss << ".dat";
+        data.url = "http://itest.5ch.net/public/newapi/client.php?subdomain=";
+        data.url += regex.str( 1 ) + "&board=" + regex.str( 2 ) + "&dat=" + regex.str( 3 );
+        data.agent = CONFIG::get_agent_for_readcgi();
+        data.ex_field = "X-Requested-With: XMLHttpRequest\r\n";
 
         // レジュームは無し
         set_resume( false );
+    }
+
+    // read.cgi使用
+    else if( m_mode == MODE_KAKO_CGI
+            || ( CONFIG::get_use_2ch_readcgi() && ! DBTREE::board_is_dedicated_server( get_url() ) ) ){
+
+        if ( m_mode != MODE_KAKO_CGI ) m_mode = MODE_CGI;
+
+        std::string url = ( m_mode == MODE_CGI ) ? get_url() : m_org_url;
+        if( ! regex.exec( "(https?://[^/]*)\\.[25]ch\\.net(/.*)/dat(/.*)\\.dat$", url, offset, icase, newline, usemigemo, wchar ) ) return;
+
+        data.url = regex.str( 1 ) + ".5ch.net/test/read.cgi" + regex.str( 2 ) + regex.str( 3 ) + "/";
+        if( id_header() ) data.url += MISC::itostr( id_header() ) + "-n";
+        data.agent = CONFIG::get_agent_for_readcgi();
 
-        data.url = ss.str();
+        // レジュームは無し
+        set_resume( false );
     }
 
-    // 普通の読み込み
-    else{
+    // 2ch API使用
+    else if( ! is_checking_update() && CORE::get_login2ch()->login_now()
+            && ! DBTREE::board_is_dedicated_server( get_url() ) ){
+
+        if( m_mode != MODE_RETRY && m_mode != MODE_KAKO_API ) m_mode = MODE_NORMAL;
+
+        std::string url = ( m_mode == MODE_KAKO_API ) ? m_org_url : get_url();
+        if( ! regex.exec( "https?://([^/\\.]+)(\\.[^/]+)(/.*)/dat(/.*)\\.dat$", url, offset, icase, newline, usemigemo, wchar ) ) return;
+
+        std::string path = "/v1/" + regex.str( 1 ) + regex.str( 3 ) + regex.str( 4 );
+        std::string sid = CORE::get_login2ch()->get_sessionid().substr( strlen( "Monazilla/1.00:" ) );
+        std::string msg = path + sid + CONFIG::get_appkey();
+        std::string post = "sid=" + sid + "&hobo=" + MISC::hmac_sha256( CONFIG::get_hmkey(), msg )
+            + "&appkey=" + CONFIG::get_appkey();
+
+        data.url = CONFIG::get_url_api2ch() + path;
+        data.str_post = post;
+        data.contenttype = "application/x-www-form-urlencoded";
+        if( ! CONFIG::get_x_2ch_ua().empty() )
+            data.ex_field = "X-2ch-UA: " + CONFIG::get_x_2ch_ua() + "\r\n";
 
         // レジューム設定
         // 1byte前からレジュームして '\n' が返ってこなかったらあぼーんがあったってこと
@@ -198,7 +261,20 @@
         }
         else set_resume( false );
 
+    }
+    // 普通の読み込み
+    else{
+
+        if( m_mode != MODE_RETRY ) m_mode = MODE_NORMAL;
         data.url = get_url();
+
+        // レジューム設定
+        // 1byte前からレジュームして '\n' が返ってこなかったらあぼーんがあったってこと
+        if( get_lng_dat() ) {
+            data.byte_readfrom = get_lng_dat() -1;
+            set_resume( true );
+        }
+        else set_resume( false );
     }
 
 #ifdef _DEBUG    
@@ -206,19 +282,30 @@
 #endif
 
 
-    data.agent = DBTREE::get_agent( get_url() );
+    if( !CONFIG::get_url_api2ch().compare( 0, CONFIG::get_url_api2ch().length(), data.url, 0, CONFIG::get_url_api2ch().length() ) ){
+        if( data.agent.empty() ) data.agent = CONFIG::get_agent_for2ch();
 
-#ifdef _DEBUG
-    std::cout << "agent = " << data.agent << std::endl;
-#endif
+        if( CONFIG::get_use_proxy_for2ch() ){
+            data.host_proxy = CONFIG::get_proxy_for2ch();
+            data.port_proxy = CONFIG::get_proxy_port_for2ch();
+        }
+        data.basicauth_proxy = CONFIG::get_proxy_basicauth_for2ch();
+    }
+    else{
+        if( data.agent.empty() ) data.agent = DBTREE::get_agent( data.url );
 
-    data.host_proxy = DBTREE::get_proxy_host( get_url() );
-    data.port_proxy = DBTREE::get_proxy_port( get_url() );
-    data.basicauth_proxy = DBTREE::get_proxy_basicauth( get_url() );
+        data.host_proxy = DBTREE::get_proxy_host( data.url );
+        data.port_proxy = DBTREE::get_proxy_port( data.url );
+        data.basicauth_proxy = DBTREE::get_proxy_basicauth( data.url );
+    }
 
     data.size_buf = CONFIG::get_loader_bufsize();
     data.timeout = CONFIG::get_loader_timeout();
 
+#ifdef _DEBUG
+    std::cout << "agent = " << data.agent << std::endl;
+#endif
+
     if( ! get_date_modified().empty() ) data.modified = get_date_modified();
 }
 
@@ -233,80 +320,741 @@
               << "mode = " << m_mode << " code = " << get_code() << std::endl;
 #endif
 
-    // 更新チェックではない、オンラインの場合は offlaw や 過去ログ倉庫から取得出来るか試みる
+    // 保留データを処理する
+    sweep_buffer();
+
+    // 更新チェックではない、オンラインの場合は read.cgi や 過去ログ倉庫から取得出来るか試みる
     if( ! is_checking_update()
         && SESSION::is_online()
-        && ( get_code() == HTTP_REDIRECT || get_code() == HTTP_MOVED_PERM || get_code() == HTTP_NOT_FOUND
-             || ( ( m_mode == MODE_OFFLAW || m_mode == MODE_OFFLAW2 )
-                     && ! get_ext_err().empty() ) // rokka or offlaw2 読み込み失敗
+        && ( get_code() == HTTP_REDIRECT || get_code() == HTTP_MOVED_PERM
+            || get_code() == HTTP_NOT_FOUND || get_code() == HTTP_NOT_IMPREMENTED
+            || ( ( m_mode == MODE_KAKO || m_mode == MODE_KAKO_GZ )
+                     && get_code() == HTTP_AUTH_REQ ) // 過去ログ読み込み失敗
+            || ( m_mode == MODE_CGI && get_code() == HTTP_ORIGN_ERR )
+            || ! get_ext_err().empty() // スレが存在しない
+            || threadstatus() == 8 // 過去ログ
             )
         ){
 
 /*
+  (1) 読み込み( dat直接, read.cgi, API )
+  (2) 過去ログ倉庫(gz)から読み込み (※1)
+    ・スレIDが10桁の場合 → http://サーバ/板ID/kako/IDの上位4桁/IDの上位5桁/ID.dat.gz
+    ・スレIDが9桁の場合 → http://サーバ/板ID/kako/IDの上位3桁/ID.dat.gz
+  (3) 過去ログ倉庫から読み込み (※2)
+    ・スレIDが10桁の場合 → http://サーバ/板ID/kako/IDの上位4桁/IDの上位5桁/ID.dat
+    ・スレIDが9桁の場合 → http://サーバ/板ID/kako/IDの上位3桁/ID.dat
+  (4) 外部のログ保存サービスから読み込み
+  (5) read.cgiで読み込んでない場合にはread.cgiからの読み込み
 
-  ・スレIDが10桁の場合 → http://サーバ/板ID/kako/IDの上位4桁/IDの上位5桁/ID.dat.gz
 
-  (例) http://HOGE.2ch.net/test/read.cgi/hoge/1234567890/ を取得
+  (※1)ただし 2008年1月1日以降に立てられたスレは除く
+  (※2)少なくても rokka導入(2013年9月27日)以降に立てられたスレ除く
 
-  (1) http://HOGE.2ch.net/hoge/dat/1234567890.dat から dat を取得。302で●がある場合(2-1)、無い場合は(2-2)へ(※)
+  (注) 古すぎる(2000年頃)のdatは形式が違う(<>ではなくて,で区切られている)ので読み込みに考慮が必要
 
-  (2-1) offlaw.cgiを使って取得
+*/
 
-  (2-2) http://HOGE.2ch.net/hoge/kako/1234/12345/1234567890.dat.gz から取得。302なら(3)へ
+        // APIが不調の場合があるので1回だけリトライ
+        if( m_mode == MODE_NORMAL && threadstatus() != 8 ){
+            m_mode = MODE_RETRY;
+        }
 
-  (3) http://HOGE.2ch.net/hoge/kako/1234/12345/1234567890.dat から取得 
+        // jsonで取得
+        else if( m_mode == MODE_CGI && get_code() == HTTP_ORIGN_ERR ){
+            m_mode = MODE_JSON;
+        }
 
+        // 浪人による過去ログ取得
+        else if( ( m_mode == MODE_NORMAL || m_mode == MODE_RETRY )
+                && CORE::get_login2ch()->login_now() && !CORE::get_login2ch()->get_username().empty() ){
+            m_mode = MODE_KAKO_API;
+        }
 
-  ・スレIDが9桁の場合 → http://サーバ/板ID/kako/IDの上位3桁/ID.dat.gz
+        // 過去ログ倉庫(gz圧縮)
+        else if( m_mode <= MODE_KAKO_API && m_since_time < 1380246829 ){
+            // ただし 2008年1月1日以降に立てられたスレは除く
+            if( m_since_time < 1199113200 ) m_mode = MODE_KAKO_GZ;
+            else m_mode = MODE_KAKO;
+        }
 
-  (例) http://HOGE.2ch.net/test/read.cgi/hoge/123456789/ を取得
+        // 過去ログ倉庫
+        else if( m_mode == MODE_KAKO_GZ ) m_mode = MODE_KAKO;
 
-  (1) http://HOGE.2ch.net/hoge/dat/1234567890.dat から dat を取得。302で●がある場合(2-1)、無い場合は(2-2)へ(※)
+        // 外部のログ保存サービス
+        else if( m_mode != MODE_KAKO_EXT && CONFIG::get_use_external_log() ) m_mode = MODE_KAKO_EXT;
 
-  (2-1) offlaw.cgiを使って取得
+        // 過去ログ取得にread.cgiを使う場合
+        else if( m_mode != MODE_KAKO_CGI
+                && ( ( ( m_org_url != get_url() || DBTREE::board_is_dedicated_server( get_url() ) )
+                        && ( CONFIG::get_use_2chkako_readcgi() || CONFIG::get_use_2ch_readcgi() ) )
+                    || ( ! CONFIG::get_use_2ch_readcgi() && CONFIG::get_use_2chkako_readcgi() ) ) )
+            m_mode = MODE_KAKO_CGI;
 
-  (2-2) http://HOGE.2ch.net/hoge/kako/123/123456789.dat.gz から取得。302なら(3)へ
+        // 失敗
+        else m_mode = MODE_NORMAL;
 
-  (3) http://HOGE.2ch.net/hoge/kako/123/123456789.dat から取得 
+        if( m_mode != MODE_NORMAL ){
+#ifdef _DEBUG
+            std::cout << "switch mode to " << m_mode << std::endl;
+#endif
+            set_date_modified( std::string() );
+            set_ext_err( std::string() );
+            download_dat( false );
+            return;
+        }
 
+        // プールしていたレスを追加
+        if( ! m_operate_info.empty() ){
+            NodeTreeBase::receive_data( m_operate_info.c_str(), m_operate_info.length() );
+        }
+    }
 
-  (※)ただし 2008年1月1日以降に立てられたスレは除く
+    m_operate_info = std::string();
 
-  (注) 古すぎる(2000年頃)のdatは形式が違う(<>ではなくて,で区切られている)ので読み込みに失敗する
+    // 過去ログから読み込んだ場合は DAT 落ちにする
+    if( m_mode >= MODE_KAKO_API || threadstatus() == 8 ){
+        set_code( HTTP_OLD );
+    }
 
-*/
+    NodeTreeBase::receive_finish();
+    m_mode = MODE_NORMAL;
 
-        // ログインしている場合は rokka 経由で旧URLで再取得
-        if( m_mode == MODE_NORMAL && CORE::get_login2ch()->login_now() ) m_mode = MODE_OFFLAW;
+    CORE::get_login2ch()->update_sid();
+}
 
-        // offlaw2.so 経由で再取得 ( bbspink を除く )
-        else if( m_mode == MODE_NORMAL && CONFIG::get_use_offlaw2_2ch()
-                && get_url().find( ".bbspink.com" ) == std::string::npos ) m_mode = MODE_OFFLAW2;
 
-        // 過去ログ倉庫(gz圧縮)
-        // ただし 2008年1月1日以降に立てられたスレは除く
-        else if( ( m_mode == MODE_NORMAL || m_mode == MODE_OFFLAW ) && m_since_time < 1199113200 ) m_mode = MODE_KAKO_GZ;
+//
+// HTMLをdatに変換する
+//
+void NodeTree2ch::html2dat( char* lines, size_t& size )
+{
+    char* ps( lines );
+    char* pd( lines );
+    char* pos;
+    char* epos;
+    int num_next = id_header() + 1;
+
+    if( ( pos = strstr( ps, "<title>" ) ) != NULL ){
+        // title
+        ps = pos + 7;
+        size_t lng;
+        if( ( epos = strstr( ps, "</title>" ) ) != NULL ) lng = epos - ps;
+        else lng = strlen( ps );
+        m_subject_2ch.assign( ps, lng );
+        ps += lng;
+        if( *( m_subject_2ch.end() - 1 ) == '\n' ) m_subject_2ch.erase( m_subject_2ch.end() - 1 );
 
-        // 過去ログ倉庫
-        else if( m_mode == MODE_KAKO_GZ ) m_mode = MODE_KAKO;
+        // 古いread.cgiでスレが見つからない
+        if( m_subject_2ch.find( "error 3001") != std::string::npos ) set_ext_err( "error" );
+    }
 
-        // 失敗
-        else m_mode = MODE_NORMAL;
+    if( ( pos = strstr( ps, "<dt>" ) ) != NULL ){
+        ps = pos;
 
+        // read.cgi 05以前の形式
+        while( ( epos = strchr( ps, '\n' ) ) != NULL ){
+            *epos = '\0';
+
+            if( ( ps = strstr( ps, "<dt>" ) ) == NULL ){
+                ps = epos + 1;
+                continue;
+            }
+
+            // number
+            pos = ps + 4;
+            while( *pos != ' ' && *pos != '\0' ) ++pos;
+            if( *pos != '\0' ) *pos = '\0'; else{ ps = epos + 1; continue; }
+            int num = atoi( ps + 4 );
+            ++pos;
+
+            // 既に読み込んでいる場合は飛ばす
+            if( num < num_next ){ ps = epos + 1; continue; }
+            while( num > num_next ){
 #ifdef _DEBUG    
-        std::cout << "switch mode to " << m_mode << std::endl;
+                std::cout << "broken down = " << num_next << std::endl;
 #endif
-        if( m_mode != MODE_NORMAL ){
-            download_dat( is_checking_update() );
-            return;
+                const char *const abone = "NG<><>NG<> NG <>\n";
+                const int lng_abone = strlen( abone );
+                if( pd + lng_abone > pos ){
+                    memcpy( pd, abone, lng_abone );
+                    pd += lng_abone;
+                }
+                num_next++;
+            }
+
+            while( !( *pos == '\x81' && *(pos + 1) == '\x46' ) && *(pos + 1) != '\0' ) ++pos;
+            if( *pos != '\x81' ){ ps = epos + 1; continue; }
+            pos += 2;
+
+            // mail
+            std::string mail;
+            if( memcmp( pos, "<a href=\"mailto:", 16 ) == 0 ){
+                pos += 16;
+                while( *pos != '\"' && *pos != '\0' ) mail.push_back( *pos++ );
+                if( *pos == '\0' ){ ps = epos + 1; continue; }
+                while( *pos != '>' && *pos != '\0' ) ++pos;
+                if( *pos == '\0' ){ ps = epos + 1; continue; }
+                else ++pos;
+            }
+
+            char* body;
+            if( ( body = strstr( pos, "<dd>" ) ) == NULL ){ ps = epos + 1; continue; }
+            else *body = '\0';
+
+            // name
+            if( memcmp( pos, "<font color=green>", 18 ) == 0 ) pos += 18;
+            if( memcmp( pos, "<b>", 3 ) == 0 ) pos += 3;
+            ps = pos;
+            pos = body;
+
+            while( !( *pos == '\x81' && *(pos + 1) == '\x46' ) ) --pos;
+            memmove( pd, ps, pos - ps ); pd += pos - ps;
+            if( memcmp( pd - 7, "</font>", 7 ) == 0 ) pd -= 7;
+            if( memcmp( pd - 4, "</a>", 4 ) == 0 ) pd -= 4;
+            if( memcmp( pd - 4, "</b>", 4 ) == 0 ) pd -= 4;
+            pos += 2;
+            *(pd++) = '<'; *(pd++) = '>';
+            mail.copy( pd, mail.length() );
+            pd += mail.length();
+            *(pd++) = '<'; *(pd++) = '>';
+
+            // date
+            ps = pos;
+            if( (pos = strstr( ps, "<a href=\"javascript:be(" ) ) != NULL ){
+                memmove( pd, ps, pos - ps );
+                pd += pos - ps;
+                ps = pos + 23;
+                *(pd++) = 'B'; *(pd++) = 'E'; *(pd++) = ':';
+                if( ( pos = strstr( ps, ");\">?" ) ) != NULL ){
+                    memmove( pd, ps, pos - ps );
+                    pd += pos - ps;
+                    ps = pos + 5;
+                    if( ( pos = strstr( ps, "</a>" ) ) != NULL ){
+                        *(pd++) = '-';
+                        memmove( pd, ps, pos - ps );
+                        pd += pos - ps;
+                    }
+                }
+            }
+            else{
+                memmove( pd, ps, body - ps );
+                pd += body - ps;
+            }
+            ps = body + 4;
+            *(pd++) = '<'; *(pd++) = '>';
+
+            // body
+            while( ( pos = strchr( ps, '<' ) ) != NULL ){
+
+                if( pos != ps ){
+                    memmove( pd, ps, pos - ps );
+                    pd += pos - ps;
+                    ps = pos;
+                }
+
+                if( memcmp( ps, "<img src=\"http", 14 ) == 0 ){
+                    // 埋め込み画像のURL
+                    ps += 14;
+                    if( memcmp( ps, "://img.5ch.net/", 15 ) == 0 ||
+                        memcmp( ps, "://img.2ch.net/", 15 ) == 0 ) memcpy( pd, "sssp", 4 );
+                    else memcpy( pd, "http", 4 );
+                    pd += 4;
+
+                    pos = strstr( ps, "\">" );
+                    if( pos == NULL ){
+                        size_t sz = strlen( ps );
+                        memmove( pd, ps, sz );
+                        ps += sz;
+                        pd += sz;
+                    }
+                    else {
+                        memmove( pd, ps, pos - ps );
+                        pd += pos - ps;
+                        ps = pos + 2;
+                    }
+                }
+
+                else if( ( memcmp( ps, "<a href=\"", 9 ) == 0 )
+                        && ( *( ps + 9 ) != '.' && *( ps + 9 ) != '/' && *( ps + 9 ) != 'j' ) ){
+                    // 外部リンクのURL
+                    if( ( pos = strchr( ps + 9, '>' ) ) != NULL ){
+                        ps = pos + 1;
+                        if( ( pos = strstr( ps, "</a>" ) ) != NULL ){
+                            memmove( pd, ps, pos - ps );
+                            pd += pos - ps;
+                            ps = pos + 4;
+                        }
+                    }
+                }
+#if 0
+                else if( memcmp( ps, "<br> <br>", 9 ) == 0 ){
+                    // 連続した改行の空白を2つにする
+                    memcpy( pd, "<br>  ", 6 ); pd += 6;
+                    ps += 5;
+                }
+#endif
+                else {
+                    *(pd++) = '<';
+                    ps += 1;
+                }
+            }
+
+            pos = ps;
+            while( *pos != '\0' ) *(pd++) = *(pos++);
+            if( memcmp( pd - 8, "<br><br>", 8 ) == 0 ) pd -= 8;
+            if( *(pd - 1) == ' ' && *(pd - 2) == ' ' ) pd -= 1;
+            *(pd++) = '<'; *(pd++) = '>';
+
+            if( num == 1 ){
+                m_subject_2ch.copy( pd, m_subject_2ch.length() );
+                pd += m_subject_2ch.length();
+            }
+            *(pd++) = '\n';
+            ++num_next;
+
+            ps = epos + 1;
         }
     }
 
-    // offlaw や 過去ログから読み込んだ場合は DAT 落ちにする
-    if( m_mode != MODE_NORMAL ){
-        m_mode = MODE_NORMAL;
-        set_code( HTTP_OLD );
+    while( ps < lines + size ){
+        epos = NULL;
+
+        while( ps < lines + size ){
+            pos = (char*)memchr( ps, '<', lines + size - ps );
+            if( pos == NULL || (lines + size) < (pos + 13) ) {
+                break;
+            }
+
+            ps = pos;
+            if( memcmp( ps, "<div class=\"", 12 ) == 0 ){
+                epos = strstr( ps, "</div>" );
+                break;
+            }
+            else if( memcmp( ps, "<span class=\"", 13 ) == 0 ){
+                epos = strstr( ps, "</span>" );
+                char *span = strstr( ps, "<span style=" );
+                if( span != NULL && span < epos ) epos = strstr( epos+7, "</span>" );
+                break;
+            }
+            else if( memcmp( ps, "<dd class=\"", 11 ) == 0 ){
+                epos = strstr( ps, "</dd>" );
+                break;
+            }
+            else if( memcmp( ps, "<dl class=\"", 11 ) == 0 ){
+                epos = strstr( ps, "</dl>" );
+                break;
+            }
+            ps++;
+        }
+        if( pos == NULL || epos == NULL ){
+            // 残りを保存する
+            if( pos != NULL ) ps = pos;
+            size = ps - lines;
+            break;
+        }
+
+        while( *ps++ != '"' );
+        if( memcmp( ps, "errorCode", 9 ) == 0 ){
+            // datが存在しません。削除されたかURL間違ってますよ。
+            set_ext_err( "error" );
+            m_tmp_num = 0;
+            m_tmp_msg.clear();
+            break;
+        }
+
+        if( memcmp( ps, "thread\"", 7 ) == 0 || memcmp( ps, "post\"", 5 ) == 0
+                || memcmp( ps, "meta\"", 5) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            m_tmp_num = 0;
+            m_tmp_msg.clear();
+            continue;
+        }
+        *epos = '\0';
+
+        // number
+        if( memcmp( ps, "number\"", 7 ) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            if( *ps != '\0' ) ++ps; else goto next_div;
+
+            m_tmp_num = atoi( ps );
+        }
+
+        // name
+        else if( memcmp( ps, "name\"", 5 ) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            if( *ps != '\0' ) ++ps; else goto next_div;
+
+            if( memcmp( ps, "<b>", 3 ) == 0 ) ps += 3;
+
+            //mail
+            std::string mail;
+            if( memcmp( ps, "<a href=\"mailto:", 16 ) == 0 ){
+                ps += 16;
+                while( *ps != '"' && *ps != '\0' ) mail.push_back( *ps++ );
+                if( *ps != '\0' ) while( *ps != '>' && *ps != '\0' ) ++ps;
+                if( *ps != '\0' ) ++ps; else goto next_div;
+
+                // read.cgiのバグ対策
+                size_t i;
+                if( (i = mail.find( "<a href=" ) ) != std::string::npos ){
+                    mail.erase( i, std::string::npos );
+                    while( *ps != '<' && *ps != '\0' ) mail.push_back( *ps++ );
+                    if( memcmp( ps, "</a>\">", 6 ) == 0 ) ps += 6;
+                }
+            }
+
+            bool fnt1_flg=false, fnt2_flg=false;
+            if( memcmp( ps, "<font color=green>", 18 ) == 0 ){
+                ps += 18;
+                fnt1_flg = true;
+            }
+            if( memcmp( ps, "<font color=\"green\">", 20 ) == 0 ){
+                ps += 20;
+                fnt2_flg = true;
+            }
+            if( memcmp( ps, "<b>", 3 ) == 0 ) ps += 3;
+            while( ( pos = strchr( ps, '<' ) ) != NULL ){
+                if( pos != ps ){
+                    m_tmp_msg.append( ps, pos - ps );
+                    ps = pos;
+                }
+
+                if( memcmp( ps, "<img src=\"", 10 ) == 0 ){
+                    // 埋め込み画像のURL
+                    ps += 10;
+                    if( memcmp( ps, "http:", 5 ) == 0 ) ps += 5;
+                    if( memcmp( ps, "//img.5ch.net/", 14 ) == 0 ||
+                        memcmp( ps, "//img.2ch.net/", 14 ) == 0 ||
+                        memcmp( ps, "//o.8ch.net/", 12 ) == 0) m_tmp_msg += "sssp:";
+                    else m_tmp_msg += "http:";
+
+                    pos = strchr( ps, '"' );
+                    if( pos == NULL ){
+                        size_t lng = strlen( ps );
+                        m_tmp_msg.append( ps, lng );
+                        ps += lng;
+                    }
+                    else {
+                        m_tmp_msg.append( ps, pos - ps );
+                        ps = pos + 2;
+                    }
+                }
+
+                else if( ( memcmp( ps, "<a href=\"", 9 ) == 0 )
+                        && ( *( ps + 9 ) != '.' && *( ps + 9 ) != '/' && *( ps + 9 ) != 'j' ) ){
+                    // 外部リンクのURL
+                    if( ( pos = strchr( ps + 9, '>' ) ) != NULL ){
+                        ps = pos + 1;
+                        if( ( pos = strstr( ps, "</a>" ) ) != NULL ){
+                            m_tmp_msg.append( ps, pos - ps );
+                            ps = pos + 4;
+                        }
+                    }
+                }
+
+                else {
+                    m_tmp_msg.push_back( '<' );
+                    ps += 1;
+                }
+            }
+            m_tmp_msg.append( ps, epos - ps );
+            if( fnt1_flg && m_tmp_msg.length() >= 7 &&
+                m_tmp_msg.compare( m_tmp_msg.length() - 7, std::string::npos, "</font>" ) == 0 )
+                m_tmp_msg.erase( m_tmp_msg.length() - 7 );
+            if( m_tmp_msg.length() >= 4 &&
+                m_tmp_msg.compare( m_tmp_msg.length() - 4, std::string::npos, "</b>" ) == 0 )
+                m_tmp_msg.erase( m_tmp_msg.length() - 4 );
+            if( fnt2_flg && m_tmp_msg.length() >= 7 &&
+                m_tmp_msg.compare( m_tmp_msg.length() - 7, std::string::npos, "</font>" ) == 0 )
+                m_tmp_msg.erase( m_tmp_msg.length() - 7 );
+            if( ! mail.empty() && m_tmp_msg.length() >= 4 &&
+                m_tmp_msg.compare( m_tmp_msg.length() - 4, std::string::npos, "</a>" ) == 0 )
+                m_tmp_msg.erase( m_tmp_msg.length() - 4 );
+            m_tmp_msg += "<>";
+            m_tmp_msg += mail;
+            m_tmp_msg += "<>";
+        }
+
+        // date
+        else if( memcmp( ps, "date\"", 5 ) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            if( *ps != '\0' ) ++ps; else goto next_div;
+
+            m_tmp_msg.append( ps, epos - ps );
+            m_tmp_msg += "<>";
+        }
+
+        // id
+        else if( memcmp( ps, "uid\"", 4 ) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            if( *ps != '\0' ) ++ps; else goto next_div;
+
+            m_tmp_msg.erase( m_tmp_msg.length() - 2 );
+            m_tmp_msg.push_back( ' ' );
+            m_tmp_msg.append( ps, epos - ps );
+            m_tmp_msg += "<>";
+        }
+
+        // be
+        else if( memcmp( ps, "be ", 3 ) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            if( *ps != '\0' ) ++ps; else goto next_div;
+
+            m_tmp_msg.erase( m_tmp_msg.length() - 2 );
+            m_tmp_msg += " BE:";
+            if( ( pos = strstr( ps, "be.5ch.net/user/" ) ) != NULL ||
+                ( pos = strstr( ps, "be.2ch.net/user/" ) ) != NULL  ){
+                ps = pos + 16;
+                if( ( pos = strchr( ps, '"' ) ) != NULL ){
+                    m_tmp_msg.append( ps, pos - ps );
+                    ps = pos + 1;
+                }
+            }
+            if( ( pos = strchr( ps, '?' ) ) != NULL ){
+                ps = pos + 1;
+                if( ( pos = strstr( ps, "</a>" ) ) != NULL ){
+                    m_tmp_msg.push_back( '-' );
+                    m_tmp_msg.append( ps, pos - ps );
+                }
+            }
+            m_tmp_msg += "<>";
+        }
+
+        // message
+        else if( memcmp( ps, "message\"", 8 ) == 0 ||
+                 memcmp( ps, "thread_in\"", 10 ) == 0 ){
+            while( *ps != '>' && *ps != '\0' ) ++ps;
+            if( *ps != '\0' ) ++ps; else goto next_div;
+
+            char *msg_end = epos;
+            if( memcmp( ps, "<span class=\"escaped\">", 22 ) == 0 ){
+                ps += 22;
+                pos = epos - 7;
+                if( memcmp( pos, "</span>", 7 ) == 0 ){
+                    *pos = '\0';
+                    msg_end = pos;
+                }
+            }
+
+            while( ( pos = strchr( ps, '<' ) ) != NULL ){
+                if( pos != ps ){
+                    m_tmp_msg.append( ps, pos - ps );
+                    ps = pos;
+                }
+
+                else if( memcmp( ps, "<img src=\"", 10 ) == 0 ){
+                    // 埋め込み画像のURL
+                    ps += 10;
+                    if( memcmp( ps, "http:", 5 ) == 0 ) ps += 5;
+                    if( memcmp( ps, "//img.5ch.net/", 14 ) == 0 ||
+                        memcmp( ps, "//img.2ch.net/", 14 ) == 0 ||
+                        memcmp( ps, "//o.8ch.net/", 12 ) == 0) m_tmp_msg += "sssp:";
+                    else m_tmp_msg += "http:";
+
+                    pos = strchr( ps, '"' );
+                    if( pos == NULL ){
+                        size_t lng = strlen( ps );
+                        m_tmp_msg.append( ps, lng );
+                        ps += lng;
+                    }
+                    else {
+                        m_tmp_msg.append( ps, pos - ps );
+                        ps = pos + 2;
+                    }
+                }
+
+                else if( ( memcmp( ps, "<a href=\"", 9 ) == 0 )
+                        && ( *( ps + 9 ) != '.' && *( ps + 9 ) != '/' && *( ps + 9 ) != 'j' ) ){
+                    // 外部リンクのURL
+                    if( ( pos = strchr( ps + 9, '>' ) ) != NULL ){
+                        ps = pos + 1;
+                        if( ( pos = strstr( ps, "</a>" ) ) != NULL ){
+                            m_tmp_msg.append( ps, pos - ps );
+                            ps = pos + 4;
+                        }
+                    }
+                }
+
+//                else if( memcmp( ps, "<br> <br>", 9 ) == 0 ){
+//                    // 連続した改行の空白を2つにする
+//                    m_tmp_msg += "<br>  ";
+//                    ps += 5;
+//                }
+
+                else {
+                    m_tmp_msg.push_back( '<' );
+                    ps += 1;
+                }
+            }
+            m_tmp_msg.append( ps, msg_end - ps );
+
+            // レス番が跳んでいる場合はNGを挿入
+            while( m_tmp_num > num_next ){
+#ifdef _DEBUG
+                std::cout << "broken down = " << num_next << std::endl;
+#endif
+                const char *const abone = "NG<><>NG<> NG <>\n";
+                const int lng_abone = strlen( abone );
+                if( pd + lng_abone < epos ){
+                    memcpy( pd, abone, lng_abone );
+                    pd += lng_abone;
+                }
+                num_next++;
+            }
+            if( m_tmp_num == num_next ){
+                // 1レス分を出力する
+                m_tmp_msg.copy( pd, m_tmp_msg.length() );
+                pd += m_tmp_msg.length();
+                //if( memcmp( pd - 8, "<br><br>", 8 ) == 0 ) pd -= 8;
+                *(pd++) = '<'; *(pd++) = '>';
+
+                if( m_tmp_num == 1 ){
+                    m_subject_2ch.copy( pd, m_subject_2ch.length() );
+                    pd += m_subject_2ch.length();
+                }
+                *(pd++) = '\n';
+                ++num_next;
+            }
+
+            m_tmp_num = 0;
+            m_tmp_msg.clear();
+        }
+
+next_div:
+        ps = epos + 1;
     }
+    *pd = '\0';
 
-    NodeTreeBase::receive_finish();
+#ifdef _DEBUG
+    std::cout << "NodeTree2ch::html2dat done. next=" << num_next << std::endl;
+#endif
 }
+
+
+//
+// JSONをdatに変換する
+//
+void NodeTree2ch::json2dat( char* buffer )
+{
+    const char* ps( buffer );
+    std::string dat;
+    dat.reserve( 100 * 1024 );
+    char ch;
+    int num_next = id_header() + 1;
+    JDLIB::Iconv icv( CHARCODE_UTF8, DBTREE::article_charcode( get_url() ) );
+
+    while( ( ch = *ps++ ) != '\0' ){
+        if( m_json_is_string ){
+            if( m_json_is_escape ){
+                if( ch == 't' ) m_json_str.push_back( '\t' );
+                else if( ch == 'u' ) m_json_codeseq = 1;
+                else m_json_str.push_back( ch ); // '/' or '\\'
+                m_json_is_escape = false;
+            }
+            else if( m_json_codeseq ){
+                if( ch >= '0' && ch <= '9' ) m_json_code = m_json_code * 16 + ch - '0';
+                else if( ch >= 'a' && ch <= 'f' ) m_json_code = m_json_code * 16 + ch - 'a' + 10;
+                else if( ch >= 'A' && ch <= 'F' ) m_json_code = m_json_code * 16 + ch - 'A' + 10;
+#ifdef _DEBUG
+                else std::cout << "broken charactor code - " << ch << std::endl;
+#endif
+
+                if( m_json_codeseq == 4 ){
+                    if( m_json_code <= 0x7F ) m_json_str.push_back( m_json_code );
+                    else {
+                        char buf[16];
+                        int lng_in;
+                        if( ( lng_in = MISC::cptoutf8( m_json_code, buf ) ) > 0 ){
+                            int lng_out;
+                            m_json_str.append( icv.convert( buf, lng_in, lng_out ) );
+                        }
+                        else{
+                            // エラーの時は数値参照にする
+                            sprintf( buf, "&#x%04x;", m_json_code );
+                            m_json_str.append( buf );
+#ifdef _DEBUG
+                            std::cout << "convert failed : ch = " << std::string(buf) << std::endl;
+#endif
+                        }
+                    }
+                    m_json_code = 0;
+                    m_json_codeseq = 0;
+                }
+                else ++m_json_codeseq;
+            }
+            else if( ch == '\\' ) m_json_is_escape = true;
+            else if( ch == '"' ) m_json_is_string = false;
+            else m_json_str.push_back( ch );
+        }
+
+        else if( ch >= '0' && ch <= '9' ) m_json_digit = m_json_digit * 10 + ( ch - '0' );
+        else if( ch == '"' ){ m_json_str.clear(); m_json_is_string = true; }
+        else if( ch == ':' ) m_json_tag = m_json_str;
+        else if( ch == '[' ) m_json_num_array = 0;
+
+        else if( ch == ',' ){
+            if( m_tmp_num == num_next ){
+                switch( m_json_num_array ){
+                    case 1: m_tmp_msg = m_json_str + "<>"; break;
+                    case 2: m_tmp_msg += m_json_str + "<>"; break;
+                    case 3:{
+                            std::string date = MISC::timettostr( atoi(m_json_str.c_str()), MISC::TIME_WEEK );
+
+                            int lng_out;
+                            m_tmp_msg += icv.convert( date.c_str(), date.length(), lng_out );
+                        }
+                        break;
+                    case 4: if( ! m_json_str.empty() ) m_tmp_msg += " ID:" + m_json_str; break;
+                    case 5: if( ! m_json_str.empty() ) m_tmp_msg += " BE:" + m_json_str; break;
+                    case 6: dat += m_tmp_msg + "<>" + m_json_str + "<>";
+                            if( m_tmp_num == 1 ) dat.append( m_subject_2ch );
+                            dat.push_back( '\n' );
+                            ++num_next;
+                    default: break;
+                }
+            }
+            else if( m_json_tag.compare( "thread" ) == 0 && m_json_num_array == 5 )
+                m_subject_2ch = m_json_str;
+            else if( m_json_tag.compare( "comments" ) == 0 && m_json_num_array == 0 ){
+                m_tmp_num = m_json_digit;
+
+                // abone
+                while( m_tmp_num > num_next ){
+#ifdef _DEBUG
+                    std::cout << "broken down = " << m_tmp_num << std::endl;
+#endif
+                    static const char broken[] = "broken";
+                    dat += broken; dat += "<><>"; dat += broken;
+                    dat += "<> "; dat += broken; dat += " <>\n";
+                    num_next++;
+                }
+            }
+            m_json_digit = 0;
+            m_json_str.clear();
+            ++m_json_num_array;
+        }
+
+        else if( ch == '{' || ch == '}' ){
+            // perser 変数の初期化
+            m_json_is_string = false;
+            m_json_is_escape = false;
+            m_json_codeseq = 0;
+            m_json_code = 0;
+            m_json_num_array = 0;
+            m_json_digit = 0;
+            m_json_str = std::string();
+            m_json_tag = std::string();
+            m_tmp_num = 0;
+            m_tmp_msg = std::string();
+        }
+
+    }
+
+    dat.copy( buffer, dat.length() );
+    *( buffer + dat.length() ) = '\0';
+#ifdef _DEBUG
+    std::cout << "NodeTree2ch::json2dat done. next=" << num_next << std::endl;
+#endif
+}
+
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetree2ch.h jd-2.8.9-a171004/src/dbtree/nodetree2ch.h
--- jd-2.8.9-150226/src/dbtree/nodetree2ch.h	2014-03-25 01:21:38.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetree2ch.h	2016-01-21 03:05:42.288143000 +0900
@@ -17,6 +17,23 @@
         std::string m_org_url;  // 移転前のオリジナルURL
         time_t m_since_time; // スレが立った時刻
         int m_mode; // 読み込みモード
+        int m_res_number_max; // 最大レス数
+
+        std::string m_subject_2ch;
+        std::string m_operate_info;
+
+        // for JSON parser
+        bool m_json_is_string;
+        bool m_json_is_escape;
+        size_t m_json_codeseq;
+        unsigned short m_json_code;
+        size_t m_json_num_array;
+        size_t m_json_digit;
+        std::string m_json_str;
+        std::string m_json_tag;
+
+        int m_tmp_num;
+        std::string m_tmp_msg;
         
       public:
 
@@ -24,15 +41,21 @@
                      const std::string& date_modified, time_t since_time );
         ~NodeTree2ch();
 
+        virtual int get_res_number_max(){ return m_res_number_max; }
+
       protected:
 
-        virtual char* process_raw_lines( char* rawlines );
+        virtual char* process_raw_lines( char* rawlines, size_t& size );
 
         virtual void create_loaderdata( JDLIB::LOADERDATA& data );
 
+        virtual void parse_extattr( const char* str, const int lng );
+
       private:
 
         virtual void receive_finish();
+        void html2dat( char* lines, size_t& size );
+        void json2dat( char* buffer );
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetree2chcompati.cpp jd-2.8.9-a171004/src/dbtree/nodetree2chcompati.cpp
--- jd-2.8.9-150226/src/dbtree/nodetree2chcompati.cpp	2014-04-28 23:43:24.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetree2chcompati.cpp	2016-01-11 11:44:52.186636000 +0900
@@ -33,7 +33,8 @@
     std::cout << "NodeTree2chCompati::~NodeTree2chCompati : " << get_url() << std::endl;
 #endif
 
-    clear();
+    // iconv 削除
+    if( m_iconv ) delete m_iconv;
 }
 
 
@@ -68,66 +69,10 @@
     NodeTreeBase::init_loading();
 
     // iconv 初期化
-    std::string charset = DBTREE::board_charset( get_url() );
-    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( charset, "UTF-8" );
-}
-
-
-
-//
-// キャッシュに保存する前の前処理
-//
-// 先頭にrawモードのステータスが入っていたら取り除く
-//
-char* NodeTree2chCompati::process_raw_lines( char* rawlines )
-{
-    char* pos = rawlines;
-
-    if( *pos == '+' || *pos == '-' || *pos == 'E' ){
-
-        int status = 0;
-        if( pos[ 1 ] == 'O' && pos[ 2 ] == 'K' ) status = 1;
-        if( pos[ 1 ] == 'E' && pos[ 2 ] == 'R' && pos[ 3 ] == 'R' ) status = 2;        
-        if( pos[ 1 ] == 'I' && pos[ 2 ] == 'N' && pos[ 3 ] == 'C' && pos[ 4 ] == 'R' ) status = 3;
-        if( pos[ 0 ] == 'E' && pos[ 1 ] == 'R' && pos[ 2 ] == 'R' && pos[ 3 ] == 'O' && pos[ 4 ] == 'R' ) status = 4;
-
-#ifdef _DEBUG
-        std::cout << "NodeTree2chCompati::process_raw_lines : raw mode status = " << status << std::endl;
-#endif
-
-        if( status != 0 ){
-            pos = skip_status_line( pos, status );
-        }
-    }
-
-    return pos;
+    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( DBTREE::article_charcode( get_url() ), CHARCODE_UTF8 );
 }
 
 
-//
-// ステータス行のスキップ処理
-//   status == 1 : 正常ステータス
-//   status != 1 : 異常ステータス
-// 
-char* NodeTree2chCompati::skip_status_line( char* pos, int status )
-{
-    // この行を飛ばす
-    char* pos_msg = pos;
-    while( *pos != '\n' && *pos != '\0' ) ++pos;
-
-    // エラー
-    if( status != 1 ){
-        int byte;
-        std::string ext_err = std::string( m_iconv->convert( pos_msg, pos - pos_msg, byte ) );
-        set_ext_err( ext_err );
-        MISC::ERRMSG( ext_err );
-    }
-
-    if( *pos == '\n' ) ++pos;
-
-    return pos;
-}
-
 
 //
 // raw データを dat 形式に変換
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetree2chcompati.h jd-2.8.9-a171004/src/dbtree/nodetree2chcompati.h
--- jd-2.8.9-150226/src/dbtree/nodetree2chcompati.h	2014-03-25 01:21:38.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetree2chcompati.h	2015-11-15 17:47:28.042205000 +0900
@@ -29,11 +29,8 @@
 
         virtual void clear();
         virtual void init_loading();
-        virtual char* process_raw_lines( char* rawlines );
         virtual const char* raw2dat( char* rawlines, int& byte );
 
-        char* skip_status_line( char* pos, int status );
-
         virtual void create_loaderdata( JDLIB::LOADERDATA& data );
     };
 }
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetreebase.cpp jd-2.8.9-a171004/src/dbtree/nodetreebase.cpp
--- jd-2.8.9-150226/src/dbtree/nodetreebase.cpp	2015-01-25 15:54:29.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetreebase.cpp	2017-10-02 02:15:52.737517019 +0900
@@ -7,10 +7,11 @@
 #include "spchar_decoder.h"
 #include "interface.h"
 
-#include "jdlib/miscutil.h"
+#include "jdlib/misccharcode.h"
+#include "jdlib/miscgtk.h"
 #include "jdlib/miscmsg.h"
+#include "jdlib/misctrip.h"
 #include "jdlib/loaderdata.h"
-#include "jdlib/jdregex.h"
 
 #include "dbimg/imginterface.h"
 
@@ -25,7 +26,9 @@
 #include "command.h"
 #include "cache.h"
 #include "session.h"
+#include "replacestrmanager.h"
 #include "urlreplacemanager.h"
+#include "cssmanager.h"
 
 #include <sstream>
 #include <fstream>
@@ -51,10 +54,11 @@
     RANGE_REF = 20,
     MAX_LINK_DIGIT = 4,  // レスアンカーでMAX_LINK_DIGIT 桁までリンクにする
 
-    MAXSISE_OF_LINES = 512 * 1024,   // ロード時に1回の呼び出しで読み込まれる最大データサイズ
-    SIZE_OF_HEAP = MAXSISE_OF_LINES + 64,
+    MAXSISE_OF_LINES = 256 * 1024,   // ロード時に1回の呼び出しで読み込まれる最大データサイズ
+    SIZE_OF_HEAP = 512 * 1024,
 
     INITIAL_RES_BUFSIZE = 128,  // レスの文字列を返すときの初期バッファサイズ
+    IDHASH_TBLSIZE = 1024,  // IDのハッシュテーブルサイズ
 };
 
 
@@ -70,11 +74,9 @@
 
 
 #define IS_URL(node) \
-( node->type == NODE_LINK && node->linkinfo->link \
-&& ( std::string( node->linkinfo->link ).find( "http" ) == 0 \
-|| std::string( node->linkinfo->link ).find( "https" ) == 0  \
-|| std::string( node->linkinfo->link ).find( "ftp" ) == 0 )  \
-)
+ ( node->type == NODE_LINK && node->linkinfo->link \
+  && ( memcmp( node->linkinfo->link, "http", 4 ) == 0 \
+      || memcmp( node->linkinfo->link, "ftp", 3 ) == 0 ) )
 
 
 using namespace DBTREE;
@@ -89,17 +91,17 @@
       m_broken( false ),
       m_heap( SIZE_OF_HEAP ),
       m_buffer_lines( NULL ),
+      m_byte_buffer_lines_left( 0 ),
       m_parsed_text( NULL ),
       m_buffer_write( NULL ),
       m_check_update( false ),
       m_check_write( false ),
       m_loading_newthread( false ),
-      m_fout ( NULL )
+      m_fout ( NULL ),
+      m_idhash( NULL )
 {
     set_date_modified( modified );
 
-    clear();
-
     // ヘッダのポインタの配列作成
     m_vec_header = ( NODE** ) m_heap.heap_alloc( sizeof( NODE* ) * MAX_RESNUMBER );
 
@@ -108,6 +110,7 @@
     NODE* tmpnode = create_node_header(); 
     m_vec_header[ m_id_header ] = tmpnode;
 
+    m_url_readcgi = url_readcgi( m_url, 0, 0 );
     m_default_noname = DBTREE::default_noname( m_url );
 
     // 参照で色を変える回数
@@ -120,9 +123,9 @@
 
     // レスにアスキーアートがあると判定する正規表現
     if( CONFIG::get_aafont_enabled() ){
-        m_aa_regex = CONFIG::get_regex_res_aa();
-    } else {
-        m_aa_regex = std::string();
+        const bool icase = false;
+        const bool newline = true;
+        m_aa_regex.set( CONFIG::get_regex_res_aa(), icase, newline );
     }
 
 #ifdef _DEBUG
@@ -162,6 +165,7 @@
 #endif
 
     m_url = url;
+    m_url_readcgi = url_readcgi( m_url, 0, 0 );
 
 #ifdef _DEBUG
     if( ! old_url.empty() ) std::cout << "NodeTreeBase::update_url from "  << old_url
@@ -240,10 +244,17 @@
     }
 
     // IDの数を数えている場合
-
-    for( int i = 1; i <= m_id_header; ++i ){
-
-        if( get_id_name( i ) == id ) return get_num_id_name( i );
+    if( m_idhash ){
+        int key = MISC::fnv_hash( id.c_str(), id.length() ) % IDHASH_TBLSIZE;
+        IDHASH* idhash = m_idhash[ key ];
+        if( idhash ){
+            while( true ){
+                const int cmp = strcmp( id.c_str(), idhash->id );
+                if( cmp == 0 ) return get_num_id_name( idhash->num );
+                if( ! idhash->child[ cmp>0 ] ) break;
+                idhash = idhash->child[ cmp>0 ];
+            }
+        }
     }
 
     return 0;
@@ -387,7 +398,7 @@
 //
 // 含まれる URL をリストにして取得
 //
-std::list< std::string > NodeTreeBase::get_urls()
+const std::list< std::string > NodeTreeBase::get_imglinks()
 {
     std::list< std::string > list_urls;
     for( int i = 1; i <= m_id_header; ++i ){
@@ -400,7 +411,8 @@
                 NODE* node = head->headinfo->block[ block ];
 
                 while( node ){
-                    if( IS_URL( node ) ) list_urls.push_back( node->linkinfo->link );
+                    if( IS_URL( node ) && node->linkinfo->imglink )
+                        list_urls.push_back( node->linkinfo->imglink );
                     node = node->next_node;
                 }
             }
@@ -555,21 +567,21 @@
     std::list< int > list_resnum;
     if( query.empty() ) return list_resnum;
 
-    std::list< JDLIB::Regex > list_regex;
+    std::list< JDLIB::RegexPattern > list_regex;
+    JDLIB::Regex regex;
     const size_t offset = 0;
     const bool icase = true; // 大文字小文字区別しない
     const bool newline = true; // . に改行をマッチさせない
     const bool usemigemo = true; // migemo使用
-    const bool wchar = true; // 全角半角の区別をしない
+    const bool wchar = false; // 全角半角の区別をしない
+    const bool norm = true; // Unicodeの互換文字を区別しない
 
     const std::list< std::string > list_query = MISC::split_line( query );
     std::list< std::string >::const_iterator it_query;
     for( it_query = list_query.begin(); it_query != list_query.end() ; ++it_query ){
 
-        const std::string &query = ( *it_query );
-        
-        list_regex.push_back( JDLIB::Regex() );
-        list_regex.back().compile( query, icase, newline, usemigemo, wchar );
+        list_regex.push_back( JDLIB::RegexPattern() );
+        list_regex.back().set( *it_query, icase, newline, usemigemo, wchar, norm );
     }
 
     for( int i = 1; i <= m_id_header ; ++i ){
@@ -579,11 +591,10 @@
         bool apnd = true;
         if( mode_or ) apnd = false;
 
-        std::list< JDLIB::Regex >::iterator it_regex;
+        std::list< JDLIB::RegexPattern >::iterator it_regex;
         for( it_regex = list_regex.begin(); it_regex != list_regex.end() ; ++it_regex ){
 
-            JDLIB::Regex &regex = ( *it_regex );
-            const bool ret = regex.exec( res_str, offset );
+            const bool ret = regex.match( *it_regex, res_str, offset );
 
             // OR
             if( mode_or ){
@@ -624,6 +635,7 @@
 node = head->headinfo->block[ id ]; \
 while( node ){ \
 if( node->type == DBTREE::NODE_BR ) str_res += "\n" + ref_prefix; \
+else if( node->type == DBTREE::NODE_SP ) str_res += " "; \
 else if( node->type == DBTREE::NODE_HTAB ) str_res += "\t"; \
 else if( node->text ) str_res += node->text; \
 node = node->next_node; \
@@ -668,53 +680,6 @@
 
 
 //
-// number 番のレスの生文字列を返す
-//
-const std::string NodeTreeBase::get_raw_res_str( int number )
-{
-#ifdef _DEBUG
-    std::cout << "NodeTreeBase::get_raw_res_str : num = " << number << std::endl;
-#endif
-    std::string retstr;
-
-    std::string str;
-    std::string path_cache = CACHE::path_dat( m_url );
-    if( ! CACHE::load_rawdata( path_cache, str ) ) return std::string();
-
-    char* rawlines = ( char* ) malloc( str.size() + 64 );
-    strcpy( rawlines, str.c_str() );
-
-    // dat形式に変換
-
-    int id_header = m_id_header;
-    m_id_header = 0;
-    init_loading();
-
-    int byte;
-    const char* datlines = raw2dat( rawlines, byte );
-    if( byte ){
-
-        std::list< std::string > lines = MISC::get_lines( datlines );
-        std::list< std::string >::iterator it = lines.begin();
-        for( int i = 1; it != lines.end() && i < number ; ++it, ++i );
-        if( it != lines.end() ) retstr = *it;
-    }
-
-#ifdef _DEBUG
-    std::cout << retstr << std::endl;
-#endif
-
-    clear();
-    m_id_header = id_header;
-    if( rawlines ) free( rawlines );
-
-    return retstr;
-}
-
-
-
-
-//
 // number番を書いた人の名前を取得
 //
 const std::string NodeTreeBase::get_name( int number )
@@ -792,10 +757,17 @@
 const std::string NodeTreeBase::get_id_name( int number )
 {
     NODE* head = res_header( number );
-    if( ! head ) return std::string();
-    if( ! head->headinfo->block[ BLOCK_ID_NAME ] ) return std::string();
+    if( ! head || ! head->headinfo->block[ BLOCK_ID_NAME ] ) return std::string();
+
+    NODE* idnode = head->headinfo->block[ BLOCK_ID_NAME ]->next_node;
+    while( idnode && ( ! idnode->linkinfo || ! idnode->linkinfo->link
+                        || idnode->linkinfo->link[ 0 ] != 'I' ) ){
+        idnode = idnode->next_node;
+    }
+
+    if( idnode ) return idnode->linkinfo->link;
 
-    return head->headinfo->block[ BLOCK_ID_NAME ]->next_node->linkinfo->link;
+    return std::string();
 }
 
 
@@ -821,6 +793,7 @@
 //
 NODE* NodeTreeBase::create_node_header()
 {
+    if( m_id_header >= MAX_RESNUMBER ) return NULL;
     ++m_id_header;
     m_node_previous = NULL;
 
@@ -870,7 +843,8 @@
 //
 NODE* NodeTreeBase::create_node_br()
 {
-    NODE* tmpnode = create_node();
+    // 改行前の空白を取り除く
+    NODE* tmpnode = ( m_node_previous->type == NODE_SP ) ? m_node_previous : create_node();
     tmpnode->type = NODE_BR;
     return tmpnode;
 }
@@ -890,32 +864,34 @@
 //
 // スペースノード
 //
-NODE* NodeTreeBase::create_node_space( const int type )
+NODE* NodeTreeBase::create_node_space( const int type, const int bg )
 {
-    NODE* tmpnode = create_node();
-    tmpnode->type = type;
-    return tmpnode;
-}
+    NODE* tmpnode;
 
+    if( type != NODE_SP ||
+            ( m_node_previous->type != type && m_node_previous->type != NODE_MULTISP
+              && m_node_previous->type != NODE_HTAB ) ){
+        tmpnode = create_node();
+        tmpnode->type = type;
+        tmpnode->color_text = COLOR_CHAR;
+        tmpnode->color_back = bg;
+        tmpnode->bold = false;
+    }
+    else{
+        tmpnode = m_node_previous;
+    }
 
-//
-// 連続半角スペース
-//
-NODE* NodeTreeBase::create_node_multispace( const char* text, const int n )
-{
-    NODE* tmpnode = create_node_ntext( text, n, COLOR_CHAR, false );
-    tmpnode->type = NODE_MULTISP;
     return tmpnode;
 }
 
 
 //
-// 水平タブノード
+// 連続半角スペース
 //
-NODE* NodeTreeBase::create_node_htab()
+NODE* NodeTreeBase::create_node_multispace( const char* text, const int n, const int bg )
 {
-    NODE* tmpnode = create_node();
-    tmpnode->type = NODE_HTAB;
+    NODE* tmpnode = create_node_ntext( text, n, COLOR_CHAR, bg, false );
+    tmpnode->type = NODE_MULTISP;
     return tmpnode;
 }
 
@@ -925,15 +901,15 @@
 //
 // bold : 太字か
 //
-NODE* NodeTreeBase::create_node_link( const char* text, const int n, const char* link, const int n_link, const int color_text, const bool bold )
+NODE* NodeTreeBase::create_node_link( const char* text, const int n, const char* link, const int n_link, const int color_text, const int color_back, const bool bold )
 {
-    NODE* tmpnode = create_node_ntext( text, n, color_text, bold );
+    NODE* tmpnode = create_node_ntext( text, n, color_text, color_back, bold );
 
     if( tmpnode ){
         tmpnode->type = NODE_LINK;
 
         // リンク情報作成
-        char *tmplink = ( char* )m_heap.heap_alloc( n_link +4 );
+        char *tmplink = ( char* )m_heap.heap_alloc( n_link + 1 );
         memcpy( tmplink, link, n_link );
         tmplink[ n_link ] = '\0';
 
@@ -953,7 +929,7 @@
                                      const int color_text,  const bool bold,
                                      const ANCINFO* ancinfo, const int lng_ancinfo )
 {
-    NODE* tmpnode = create_node_link( text, n, link, n_link, color_text, bold );
+    NODE* tmpnode = create_node_link( text, n, link, n_link, color_text, COLOR_NONE, bold );
     if( tmpnode ){
 
         tmpnode->linkinfo->ancinfo = ( ANCINFO* )m_heap.heap_alloc( sizeof( ANCINFO ) * ( lng_ancinfo + 1 ) );
@@ -973,7 +949,7 @@
     tmpnode->type = NODE_SSSP;
 
     // リンク情報作成
-    char *tmplink = ( char* )m_heap.heap_alloc( n_link +4 );
+    char *tmplink = ( char* )m_heap.heap_alloc( n_link + 1 );
     memcpy( tmplink, link, n_link );
     tmplink[ n_link ] = '\0';
 
@@ -992,7 +968,7 @@
 //
 NODE* NodeTreeBase::create_node_img( const char* text, const int n, const char* link, const int n_link, const int color_text,  const bool bold )
 {
-    NODE* tmpnode = create_node_link( text, n, link, n_link, color_text, bold );
+    NODE* tmpnode = create_node_link( text, n, link, n_link, color_text, COLOR_NONE, bold );
     if( tmpnode ){
         tmpnode->linkinfo->image = true;
         tmpnode->linkinfo->imglink = tmpnode->linkinfo->link;
@@ -1007,11 +983,11 @@
 //
 NODE* NodeTreeBase::create_node_thumbnail( const char* text, const int n, const char* link, const int n_link, const char* thumb, const int n_thumb, const int color_text, const bool bold )
 {
-    NODE* tmpnode = create_node_link( text, n, link, n_link, color_text, bold );
+    NODE* tmpnode = create_node_link( text, n, link, n_link, color_text, COLOR_NONE, bold );
 
     if( tmpnode ){
         // サムネイル画像のURLをセット
-        char *tmpthumb = ( char* )m_heap.heap_alloc( n_thumb +4 );
+        char *tmpthumb = ( char* )m_heap.heap_alloc( n_thumb + 1 );
         memcpy( tmpthumb, thumb, n_thumb );
         tmpthumb[ n_thumb ] = '\0';
 
@@ -1028,7 +1004,7 @@
 //
 NODE* NodeTreeBase::create_node_text( const char* text, const int color_text, const bool bold )
 {
-    return create_node_ntext( text, strlen( text ), color_text, bold );
+    return create_node_ntext( text, strlen( text ), color_text, COLOR_NONE, bold );
 }
 
 
@@ -1036,7 +1012,7 @@
 //
 // テキストノード作成( サイズ指定 )
 //
-NODE* NodeTreeBase::create_node_ntext( const char* text, const int n, const int color_text, const bool bold )
+NODE* NodeTreeBase::create_node_ntext( const char* text, const int n, const int color_text, const int color_back, const bool bold )
 {
     if( n <= 0 ) return NULL;
     
@@ -1045,9 +1021,10 @@
     if( tmpnode ){
         tmpnode->type = NODE_TEXT;
 
-        tmpnode->text = ( char* )m_heap.heap_alloc( n +8 );
+        tmpnode->text = ( char* )m_heap.heap_alloc( n + 1 );
         memcpy( tmpnode->text, text, n ); tmpnode->text[ n ] = '\0';
         tmpnode->color_text = color_text;
+        tmpnode->color_back = color_back;
         tmpnode->bold = bold;
     }
 
@@ -1078,8 +1055,7 @@
 
     const bool digitlink = false;
     const bool bold = false;
-    const bool ahref = true;
-    parse_html( html.c_str(), html.length(), COLOR_CHAR, digitlink, bold, ahref );
+    parse_html( html.c_str(), html.length(), COLOR_CHAR, digitlink, bold );
 
     clear();
 
@@ -1120,19 +1096,16 @@
         std::string str;
         if( CACHE::load_rawdata( path_cache, str ) ){
 
-            const char* data = str.data();
-            size_t size = 0;
             m_check_update = false;
             m_check_write = false;
             m_loading_newthread = false;
             set_resume( false );
             init_loading();
+
+            const char* data = str.data();
             const size_t str_length = str.length();
-            while( size < str_length ){
-                size_t size_tmp = MIN( MAXSISE_OF_LINES - m_byte_buffer_lines_left, str_length - size );
-                receive_data( data + size, size_tmp );
-                size += size_tmp;
-            }
+
+            receive_data( data, str_length );
             receive_finish();
 
             // レジューム時のチェックデータをキャッシュ
@@ -1153,7 +1126,7 @@
 
         // レジューム時のチェック用に生データの先頭から RESUME_CHKSIZE バイト分をコピーしておく
         // 詳しくは NodeTreeBase::receive_data() を参照せよ
-        const size_t length_chk = MIN( RESUME_CHKSIZE, length );
+        const size_t length_chk = MIN( (RESUME_CHKSIZE - 1), length );
         memcpy( m_resume_head, data, length_chk );
         m_resume_head[ length_chk ] = '\0';
     }
@@ -1165,8 +1138,6 @@
 //
 void NodeTreeBase::init_loading()
 {
-    clear();
-
     // 一時バッファ作成
     if( ! m_buffer_lines ) m_buffer_lines = ( char* ) malloc( MAXSISE_OF_LINES ); 
     if( ! m_parsed_text ) m_parsed_text = ( char* ) malloc( MAXSISE_OF_LINES );
@@ -1255,6 +1226,7 @@
     if( m_check_update ){
         data.head = true;
         data.timeout = CONFIG::get_loader_timeout_checkupdate();
+        if( data.byte_readfrom ) data.byte_readfrom += 1;
     }
 
     if( data.url.empty() || ! start_load( data ) ){
@@ -1269,8 +1241,6 @@
 //
 void NodeTreeBase::receive_data( const char* data, size_t size )
 {
-    // BOF防止
-    size = MIN( MAXSISE_OF_LINES, size );
 
     if( is_loading()
         && ( get_code() != HTTP_OK && get_code() != HTTP_PARTIAL_CONTENT ) ){
@@ -1320,32 +1290,61 @@
 
     if( !size ) return;
     
-    // バッファが '\n' で終わるように調整
-    const char* pos = data + size;
-    if( *pos == '\0' ) --pos; // '\0' を除く
-    while( *pos != '\n' && pos != data ) --pos;
-
-    // 前回の残りのデータに新しいデータを付け足して add_raw_lines()にデータを送る
-    size_t size_in = ( int )( pos - data );
-    if( size_in > 0 ){
-        size_in ++; // '\n'を加える
-        memcpy( m_buffer_lines + m_byte_buffer_lines_left , data, size_in );
-        m_buffer_lines[ m_byte_buffer_lines_left + size_in ] = '\0';
-        add_raw_lines( m_buffer_lines, m_byte_buffer_lines_left + size_in );
-    }
+    while( size > 0 ){
 
-    // add_raw_lines() でレジュームに失敗したと判断したら、バッファをクリアする
-    if( m_resume == RESUME_FAILED ){
-        m_byte_buffer_lines_left = 0;
-        return;
+        // BOF防止
+        size_t size_in = MIN( MAXSISE_OF_LINES - m_byte_buffer_lines_left - 1, size );
+
+        // バッファが '\n' で終わるように調整
+        const char* pos = data + size_in;
+        while( pos != data && *(pos - 1) != '\n' ) --pos;
+
+        if( pos != data ) size_in = pos - data;
+        // バッファサイズの半分までは改行を待ってみる
+        else if( ( m_byte_buffer_lines_left + size ) < ( MAXSISE_OF_LINES / 2 ) ) size_in = 0;
+
+        if( size_in > 0 ){
+            // 前回の残りのデータに新しいデータを付け足して add_raw_lines()にデータを送る
+            memcpy( m_buffer_lines + m_byte_buffer_lines_left, data, size_in );
+            m_buffer_lines[ m_byte_buffer_lines_left + size_in ] = '\0';
+            size_t lng = add_raw_lines( m_buffer_lines, m_byte_buffer_lines_left + size_in );
+            m_byte_buffer_lines_left = m_byte_buffer_lines_left + size_in - lng;
+            memmove( m_buffer_lines, m_buffer_lines + lng, m_byte_buffer_lines_left );
+            data += size_in;
+            size -= size_in;
+        }
+        else break;
+
+        // add_raw_lines() でレジュームに失敗したと判断したら、バッファをクリアする
+        if( m_resume == RESUME_FAILED ){
+            m_byte_buffer_lines_left = 0;
+            return;
+        }
     }
 
     // 残りの分をバッファにコピーしておく
-    m_byte_buffer_lines_left = size - size_in;
-    if( m_byte_buffer_lines_left ) memcpy( m_buffer_lines, data + size_in, m_byte_buffer_lines_left );
+    if( size > 0 ){
+        memcpy( m_buffer_lines + m_byte_buffer_lines_left, data, size );
+        m_byte_buffer_lines_left += size;
+    }
 }
 
 
+//
+// 保留された受信データのはき出し
+//
+void NodeTreeBase::sweep_buffer()
+{
+    // 特殊スレのdatには、最後の行に'\n'がない場合がある
+    if( m_byte_buffer_lines_left > 0 ){
+        // 正常に読込完了した場合で、バッファが残っていれば add_raw_lines()にデータを送る
+        m_buffer_lines[ m_byte_buffer_lines_left ] = '\0';
+        add_raw_lines( m_buffer_lines, m_byte_buffer_lines_left );
+        // バッファをクリア
+        m_byte_buffer_lines_left = 0;
+    }
+}
+
 
 //
 // ロード完了
@@ -1357,6 +1356,7 @@
         && get_code() != HTTP_OK
         && get_code() != HTTP_PARTIAL_CONTENT
         && get_code() != HTTP_NOT_MODIFIED
+        && get_code() != HTTP_RANGE_ERR
         && get_code() != HTTP_OLD
         ){
         is_error = true;
@@ -1364,26 +1364,19 @@
         std::ostringstream err;
         err << m_url << std::endl
             << "load failed. : " << get_str_code();
-        if( get_code() == HTTP_REDIRECT || get_code() == HTTP_REDIRECT ) err << " location = " << location();
+        if( get_code() == HTTP_MOVED_PERM || get_code() == HTTP_REDIRECT ) err << " location = " << location();
         MISC::ERRMSG( err.str() );
     }
 
-    if( ! m_check_update ){
+    // 保留データの処理
+    if( ! is_error ) sweep_buffer();
+    else m_byte_buffer_lines_left = 0;
 
-        if( ! is_error ){
-            // 特殊スレのdatには、最後の行に'\n'がない場合がある
-            if( m_byte_buffer_lines_left > 0 ){
-                // 正常に読込完了した場合で、バッファが残っていれば add_raw_lines()にデータを送る
-                m_buffer_lines[ m_byte_buffer_lines_left ] = '\0';
-                add_raw_lines( m_buffer_lines, m_byte_buffer_lines_left );
-                // バッファをクリア
-                m_byte_buffer_lines_left = 0;
-            }
-        }
+    if( ! m_check_update ){
 
         // Requested Range Not Satisfiable
         if( get_code() == HTTP_RANGE_ERR ){
-            m_broken = true;
+//            m_broken = true;
             MISC::ERRMSG( "Requested Range Not Satisfiable" );
         }
 
@@ -1421,7 +1414,14 @@
 
     if( ! m_check_update
         && ( get_code() == HTTP_OK || get_code() == HTTP_PARTIAL_CONTENT )
-        && ! get_date_modified().empty() ) CACHE::set_filemtime( CACHE::path_dat( m_url ), get_time_modified() );
+        && ! get_date_modified().empty() ){
+
+        CACHE::set_filemtime( CACHE::path_dat( m_url ), get_time_modified() );
+
+        // クッキーのセット
+        const::std::list< std::string > list_cookies = SKELETON::Loadable::cookies();
+        DBTREE::board_set_list_cookies_for_write( m_url, list_cookies );
+    }
 
     m_check_update = false;
     m_check_write = false;
@@ -1432,22 +1432,24 @@
 //
 // 鯖から生の(複数)行のデータを受け取ってdat形式に変換して add_one_dat_line() に出力
 //
-void NodeTreeBase::add_raw_lines( char* rawlines, size_t size )
+size_t NodeTreeBase::add_raw_lines( char* rawlines, size_t size )
 {
     // 時々サーバ側のdatファイルが壊れていてデータ中に \0 が
     // 入っている時があるので取り除く
     for( size_t i = 0; i < size; ++i ){
         if( rawlines[ i ] == '\0' ){
-            MISC::ERRMSG( "EOF was inserted in the middle of the raw data" );
-            rawlines[ i ] = ' ';
+            size_t beg = i;
+            while( i < size && rawlines[ i ] == '\0' ) ++i;
+            MISC::ERRMSG( MISC::itostr( i - beg ) + " EOF was inserted in the middle of the raw data" );
+            memset( rawlines + beg, ' ', i - beg );
         } 
     }
 
     // 保存前にrawデータを加工
-    rawlines = process_raw_lines( rawlines );
+    rawlines = process_raw_lines( rawlines, size );
 
     size_t lng = strlen( rawlines );
-    if( ! lng ) return;
+    if( ! lng ) return size;
 
     // サーバが range を無視してデータを送ってきたときのレジューム処理
     if( m_resume == RESUME_MODE2 ){
@@ -1457,7 +1459,7 @@
 #endif
 
         // 先頭からdatを送ってきたかチェック
-        const size_t length_chk = MIN( lng, MIN( RESUME_CHKSIZE, strlen( m_resume_head ) ) );
+        const size_t length_chk = MIN( lng, MIN( (RESUME_CHKSIZE - 1), strlen( m_resume_head ) ) );
         if( strncmp( rawlines, m_resume_head, length_chk ) == 0 ){
             m_resume = RESUME_MODE3;
             m_resume_lng = 0;
@@ -1469,7 +1471,7 @@
             m_broken = true;
             MISC::ERRMSG( "failed to resume" );
             m_resume = RESUME_FAILED;
-            return;
+            return size;
         }
     }
 
@@ -1482,7 +1484,7 @@
 #endif
 
         m_resume_lng += lng;
-        if( m_resume_lng <= m_lng_dat ) return;
+        if( m_resume_lng <= m_lng_dat ) return size;
 
         // 越えた分をカットしてレジューム処理終了
         rawlines += ( lng  - ( m_resume_lng - m_lng_dat ) );
@@ -1494,7 +1496,7 @@
 #endif
     }
 
-    if( ! lng ) return;
+    if( ! lng ) return size;
 
     m_lng_dat += lng;
 
@@ -1513,7 +1515,7 @@
     // dat形式に変換
     int byte;
     const char* datlines = raw2dat( rawlines, byte );
-    if( !byte ) return;
+    if( !byte ) return size;
 
     // '\n' 単位で区切って add_one_dat_line() に渡す
     int num_before = m_id_header;
@@ -1538,6 +1540,7 @@
         // articlebase クラスに状態が変わったことを知らせる
         m_sig_updated.emit();
     }
+    return size;
 }
 
 
@@ -1557,6 +1560,10 @@
 
     int i;
     NODE* header = create_node_header();
+    if( header == NULL ){
+        size_t len = strlen( datline );
+        return datline + len;
+    }
     m_vec_header[ m_id_header ] =  header;
 
     // レス番号
@@ -1565,7 +1572,7 @@
     snprintf( tmplink, LNG_RES,"%s%d", PROTO_RES, header->id_header );    
 
     header->headinfo->block[ BLOCK_NUMBER ] = create_node_block();
-    create_node_link( tmpstr, strlen( tmpstr ) , tmplink, strlen( tmplink ), COLOR_CHAR_LINK_RES, true );
+    create_node_link( tmpstr, strlen( tmpstr ) , tmplink, strlen( tmplink ), COLOR_CHAR_LINK_RES, COLOR_NONE, true );
 
     const char* section[ SECTION_NUM ];
     int section_lng[ SECTION_NUM ];
@@ -1574,57 +1581,89 @@
     for( i = 0; i < SECTION_NUM ; ++i ){
         
         section[ i ] = pos;
-        while( !( *pos == '<' && *( pos + 1 ) == '>' ) && ( *pos != '\0' && *pos != '\n' ) ) ++pos;
-        section_lng[ i ] = ( int )( pos - section[ i ] );
+        while( *pos != '\0' && *pos != '\n' && !( *pos == '<' && *( pos + 1 ) == '>' ) ) ++pos;
+        section_lng[ i ] = pos - section[ i ];
         
-        if( ( *pos == '\0' || *pos == '\n' ) && i < SECTION_NUM -1 ) break; // 壊れてる
-
-        if ( !( *pos == '\0' || *pos == '\n' ) ) pos += 2; // "<>"の分
+        if( *pos == '\0' || *pos == '\n' ){ ++i; break; }
+        else pos += 2; // "<>"の分
     }
    
-    // 壊れている
-    if( i != SECTION_NUM ){
-
-#ifdef _DEBUG
-        std::cout << header->id_header << " is broken section = " << i << std::endl;
-        std::cout << datline << std::endl;
-#endif
-
-        m_broken = true;
-        header->headinfo->block[ BLOCK_NAME ] = create_node_block();
+    if( i < ( SECTION_NUM - 1) ){
+        // 本文途中まで解析出来てなければ旧dat形式でチェックしてみる
+        pos = datline;
 
-        create_node_text( " 壊れています", COLOR_CHAR );
+        for( i = 0; i < SECTION_NUM ; ++i ){
 
-        header->headinfo->block[ BLOCK_MES ] = create_node_block();
-        const char str_broken[] = "ここ";
-        create_node_link( str_broken, strlen( str_broken ) , PROTO_BROKEN, strlen( PROTO_BROKEN ), COLOR_CHAR_LINK, false );
-        create_node_text( "をクリックしてスレを再取得して下さい。", COLOR_CHAR );
+            section[ i ] = pos;
+            while( *pos != ',' && *pos != '\0' && *pos != '\n' ) ++pos;
+            section_lng[ i ] = pos - section[ i ];
         
-        return pos;
+            if( *pos == '\0' || *pos == '\n' ){ ++i; break; }
+            else pos += 1; // ","の分
+        }
     }
     
+    // 行末まで読み飛ばす
+    while( *pos != '\0' && *pos != '\n' ) ++pos;
+
     // 名前
     int color_name = COLOR_CHAR_NAME;
     if( ! section_lng[ 1 ] ) color_name = COLOR_CHAR_NAME_NOMAIL;
     parse_name( header, section[ 0 ], section_lng[ 0 ], color_name );
     
     // メール
-    parse_mail( header, section[ 1 ], section_lng[ 1 ] );
+    if( i > 1 ) parse_mail( header, section[ 1 ], section_lng[ 1 ] );
 
     // 日付とID
-    parse_date_id( header, section[ 2 ], section_lng[ 2 ] );
+    if( i > 2 ) parse_date_id( header, section[ 2 ], section_lng[ 2 ] );
 
     // 本文
-    header->headinfo->block[ BLOCK_MES ] = create_node_block();
-
     const bool digitlink = false;
     const bool bold = false;
-    const bool ahref = false;
-    parse_html( section[ 3 ], section_lng[ 3 ], COLOR_CHAR, digitlink, bold, ahref );
+    if( i > 3 ){
+        // EXTDAT情報を取得
+        if( header->id_header == 1 ) parse_extattr( section[ 3 ], section_lng[ 3 ] );
+
+        header->headinfo->block[ BLOCK_MES ] = create_node_block();
+
+        std::string str_msg;
+        const char* str = section[ 3 ];
+        int lng_msg = section_lng[ 3 ];
+
+        // 文字列置換
+        CORE::ReplaceStr_Manager* mgr = CORE::get_replacestr_manager();
+        if( mgr->list_size( CORE::REPLACETARGET_MESSAGE ) ){
+            str_msg = mgr->replace( str, lng_msg, CORE::REPLACETARGET_MESSAGE );
+            str = str_msg.c_str();
+            lng_msg = str_msg.length();
+        }
+
+        parse_html( str, lng_msg, COLOR_CHAR, digitlink, bold );
+    }
+
+    // 壊れている
+    if( i != SECTION_NUM ){
+
+#ifdef _DEBUG
+        std::cout << header->id_header << " is broken section = " << i << std::endl;
+        std::cout << datline << std::endl;
+#endif
+
+        m_broken = true;
+
+        if( i <= 2 ) header->headinfo->block[ BLOCK_MES ] = create_node_block();
+        parse_html( "<br> <br> 壊れています<br>", 32, COLOR_CHAR, digitlink, true );
+
+        const char str_broken[] = "ここ";
+        create_node_link( str_broken, strlen( str_broken ) , PROTO_BROKEN, strlen( PROTO_BROKEN ), COLOR_CHAR_LINK, COLOR_NONE, false );
+        create_node_text( "をクリックしてスレを再取得して下さい。", COLOR_CHAR );
+
+        return pos;
+    }
 
     // サブジェクト
     if( header->id_header == 1 ){
-        m_subject = std::string( section[ 4 ] ).substr( 0, section_lng[ 4 ] );
+        m_subject.assign( section[ 4 ], section_lng[ 4 ] );
 
 #ifdef _DEBUG
         std::cout << "subject = " << m_subject << std::endl;
@@ -1667,11 +1706,22 @@
 //
 void NodeTreeBase::parse_name( NODE* header, const char* str, const int lng, const int color_name )
 {
-    bool digitlink = true;
-    const bool bold = true;
-    const bool ahref = false;
+    std::string str_name;
+    int lng_name = lng;
 
-    const bool defaultname = ( strncmp( m_default_noname.data(), str, lng ) == 0 );
+    // 後ろの空白を除く
+    while( str[ lng_name - 1 ] == ' ' ) --lng_name;
+
+    const bool defaultname = ( m_default_noname.length() == ( size_t )lng_name
+            && m_default_noname.compare( 0, lng_name, str, lng_name ) == 0 );
+
+    // 文字列置換
+    CORE::ReplaceStr_Manager* mgr = CORE::get_replacestr_manager();
+    if( mgr->list_size( CORE::REPLACETARGET_NAME ) ){
+        str_name = mgr->replace( str, lng_name, CORE::REPLACETARGET_NAME );
+        str = str_name.c_str();
+        lng_name = str_name.length();
+    }
 
     header->headinfo->block[ BLOCK_NAMELINK ] = create_node_block();
 
@@ -1679,42 +1729,44 @@
     if( defaultname ) create_node_text( "名前", COLOR_CHAR );
     else{
         const char namestr[] = "名前";
-        create_node_link( namestr, strlen( namestr ) , PROTO_NAME, strlen( PROTO_NAME ), COLOR_CHAR, false );
+        create_node_link( namestr, strlen( namestr ) , PROTO_NAME, strlen( PROTO_NAME ), COLOR_CHAR, COLOR_NONE, false );
     }
 
     NODE* node = header->headinfo->block[ BLOCK_NAME ] = create_node_block();
 
     // デフォルト名無しと同じときはアンカーを作らない
     if( defaultname ){
-        digitlink = false;
-        parse_html( str, lng, color_name, digitlink, bold, ahref );
+        const bool digitlink = false;
+        const bool bold = true;
+        parse_html( str, lng_name, color_name, digitlink, bold );
     }
     else{
 
         int pos = 0;
         int i;
 
-        while( pos < lng ){
+        while( pos < lng_name ){
 
             // トリップなど</b>〜<b>の中の文字列は色を変えて数字をリンクにしない
-            for( i = pos; i < lng; ++i ){
+            for( i = pos; i < lng_name; ++i ){
                 if( str[ i ] == '<'
                     && str[ i+1 ] == '/'
                     && ( str[ i+2 ] == 'b' || str[ i+2 ] == 'B' )
-                    && str[ i+3 ] == '>' ) break;
+                    && str[ i+3 ] == '>' ){ i += 4; break; }
             }
 
             // </b>の前までパース
             if( i != pos ){
-                digitlink = true;
-                parse_html( str + pos, i - pos, color_name, digitlink, bold, ahref );
+                const bool digitlink = true;
+                const bool bold = true;
+                parse_html( str + pos, i - pos, color_name, digitlink, bold );
             }
-            if( i >= lng ) break;
-            pos = i + 4; // 4 = strlen( "</b>" );
+            if( i >= lng_name ) break;
+            pos = i;
 
             // <b>の位置を探す
-            int pos_end = lng;
-            for( i = pos; i < lng; ++i ){
+            int pos_end = lng_name;
+            for( i = pos; i < lng_name; ++i ){
                 if( str[ i ] == '<'
                     && ( str[ i+1 ] == 'b' || str[ i+1 ] == 'B' )
                     && str[ i+2 ] == '>' ){
@@ -1733,29 +1785,26 @@
 #endif
 
             // </b><b>の中をパース
-            digitlink = false; // 数字が入ってもリンクしない
-            parse_html( str + pos, pos_end - pos, COLOR_CHAR_NAME_B, digitlink, bold, ahref );
+            const bool digitlink = false; // 数字が入ってもリンクしない
+            const bool bold = false;
+            parse_html( str + pos, pos_end - pos, COLOR_CHAR_NAME_B, digitlink, bold );
 
-            pos = pos_end + 3; // 3 = strlen( "<b>" );
+            pos = pos_end;
         }
     }
 
     // plainな名前取得
     // 名前あぼーんや名前抽出などで使用する
-    if( defaultname ){
-        header->headinfo->name = ( char* )m_heap.heap_alloc( lng +2 );
-        memcpy( header->headinfo->name, str, lng );
-    }
-    else{
-        std::string str_tmp;
+    std::string str_tmp;
+    node = node->next_node;
+    while( node ){
+        if( node->type == NODE_TEXT && node->text ) str_tmp += node->text;
+        if( node->type == NODE_SP ) str_tmp += " ";
         node = node->next_node;
-        while( node ){
-            if( node->text ) str_tmp += node->text;
-            node = node->next_node;
-        }
-        header->headinfo->name = ( char* )m_heap.heap_alloc( str_tmp.length() +2 );
-        memcpy( header->headinfo->name, str_tmp.c_str(), str_tmp.length() );
     }
+    header->headinfo->name = ( char* )m_heap.heap_alloc( str_tmp.length() + 1 );
+    memcpy( header->headinfo->name, str_tmp.c_str(), str_tmp.length() );
+    header->headinfo->name[ str_tmp.length() ] = '\0';
 }
 
 
@@ -1776,16 +1825,25 @@
 
     header->headinfo->block[ BLOCK_MAIL ] = create_node_block();
 
-    if( ! lng )  create_node_text( "[]", color );
+    // 文字列置換
+    std::string str_mail;
+    int lng_mail = lng;
+    CORE::ReplaceStr_Manager* mgr = CORE::get_replacestr_manager();
+    if( mgr->list_size( CORE::REPLACETARGET_MAIL ) ){
+        str_mail = mgr->replace( str, lng, CORE::REPLACETARGET_MAIL );
+        str = str_mail.c_str();
+        lng_mail = str_mail.length();
+    }
 
+    if( lng_mail == 0 ){
+        create_node_text( "[]", color );
+    }
     else{
-        create_node_text( "[", color );
-
         const bool digitlink = true;
         const bool bold = false;
-        const bool ahref = false;
-        parse_html( str, lng, color, digitlink, bold, ahref );
 
+        create_node_text( "[", color );
+        parse_html( str, lng_mail, color, digitlink, bold );
         create_node_text( "]", color );
     }
 }
@@ -1796,30 +1854,47 @@
 //
 void NodeTreeBase::parse_date_id( NODE* header, const char* str, const int lng )
 {
+    std::string str_date;
+    int lng_date = lng;
+
+    // 文字列置換
+    CORE::ReplaceStr_Manager* mgr = CORE::get_replacestr_manager();
+    if( mgr->list_size( CORE::REPLACETARGET_DATE ) ){
+        str_date = mgr->replace( str, lng, CORE::REPLACETARGET_DATE );
+        str = str_date.c_str();
+        lng_date = str_date.length();
+    }
+
+    const bool digitlink = false;
+    const bool bold = false;
+
     int start = 0;
     int lng_text = 0;
     int lng_link_tmp;
     char tmplink[ LNG_LINK ];
 
-    int lng_id_tmp;
-    char tmpid[ LNG_ID ];
-
     header->headinfo->block[ BLOCK_DATE ] = create_node_block();
 
     for(;;){
 
         // 先頭の空白を飛ばす
-        while( start + lng_text < lng && str[ start + lng_text ] == ' ' ) ++lng_text;
+        while( start + lng_text < lng_date && str[ start + lng_text ] == ' ' ) ++lng_text;
 
         // 空白ごとにブロック分けしてパースする
         int start_block = start + lng_text;
         int lng_block = 0; // ブロックの長さ
-        while( start_block + lng_block < lng && str[ start_block + lng_block ] != ' ' ) ++lng_block;
+        while( start_block + lng_block < lng_date && str[ start_block + lng_block ] != ' ' ){
+            if( str[ start_block + lng_block ] == '<' )
+                while( start_block + lng_block < lng_date &&
+                    str[ start_block + lng_block ] != '>' ) ++lng_block;
+            else
+                ++lng_block;
+        };
+
         if( !lng_block ) break;
 
-        if(
-            // ID ( ??? の時は除く )
-            ( str[ start_block ] == 'I' && str[ start_block + 1 ] == 'D' && str[ start_block + 3 ] != '?' )
+        if( // ID
+            ( str[ start_block ] == 'I' && str[ start_block + 1 ] == 'D' )
 
             // HOST
             || ( str[ start_block + 0 ] == 'H' && str[ start_block + 1 ] == 'O' && str[ start_block + 2 ] == 'S' && str[ start_block + 3 ] == 'T' )
@@ -1834,8 +1909,7 @@
 
             // フラッシュ
             if( lng_text ){
-                if( *( str + start + lng_text - 1 ) == ' ' ) --lng_text;
-                create_node_ntext( str + start, lng_text, COLOR_CHAR );
+                parse_html( str + start, lng_text, COLOR_CHAR, digitlink, bold );
             }
 
             int offset = 0;
@@ -1844,19 +1918,18 @@
                 offset = 5;
 
                 // HOST: の場合は途中で空白が入るときがあるので最後までブロックを伸ばす
-                lng_block = lng - start_block;
+                lng_block = lng_date - start_block;
             }
             else if( str[ start_block ] == (char)0xe7 ) offset = 10;
 
             // id 取得
-            lng_id_tmp = MIN( lng_block, LNG_ID - 16 );
-            memcpy( tmpid, str + start_block, lng_id_tmp );
-            tmpid[ lng_id_tmp ] = '\0';
+            int lng_id = MIN( lng_block, (int)( LNG_ID - sizeof( PROTO_ID ) ) );
             
             // リンク文字作成
             memcpy( tmplink, PROTO_ID, sizeof( PROTO_ID ) );
-            memcpy( tmplink + sizeof( PROTO_ID ) - 1, tmpid, lng_id_tmp + 1 );
-            lng_link_tmp = strlen( tmplink );
+            memcpy( tmplink + sizeof( PROTO_ID ) - 1, str + start_block, lng_id );
+            lng_link_tmp = sizeof( PROTO_ID ) - 1 + lng_id;
+            tmplink[ lng_link_tmp ] = '\0';
 
             // 後ろに●が付いていたら取り除く
             if( tmplink[ lng_link_tmp - 3 ] == (char)0xe2 && tmplink[ lng_link_tmp - 2 ] == (char)0x97 && tmplink[ lng_link_tmp - 1 ] == (char)0x8f ){
@@ -1866,8 +1939,8 @@
 
             // リンク作成
             header->headinfo->block[ BLOCK_ID_NAME ] = create_node_block();
-            create_node_link( tmpid, offset, tmplink, lng_link_tmp, COLOR_CHAR, false );
-            create_node_ntext( tmpid +offset, lng_id_tmp -offset, COLOR_CHAR);
+            create_node_link( str + start_block, offset, tmplink, lng_link_tmp, COLOR_CHAR, COLOR_NONE, false );
+            create_node_ntext( str + start_block + offset, lng_id - offset, COLOR_CHAR );
 
             // 発言回数ノード作成
             create_node_idnum();
@@ -1880,26 +1953,33 @@
         // BE:
         else if( str[ start_block ] == 'B' && str[ start_block + 1 ] == 'E' ){
 
-            const int strlen_of_BE = 3; // = strlen( "BE:" );
+            const int strlen_of_BE = strlen( "BE:" );
 
             // フラッシュ
-            if( lng_text ) create_node_ntext( str + start, lng_text, COLOR_CHAR );
+            if( lng_text ){
+                parse_html( str + start, lng_text, COLOR_CHAR, digitlink, bold );
+            }
 
             // id 取得
             int lng_header = 0;
             while( str[ start_block + lng_header ] != '-' && lng_header < lng_block ) ++lng_header;
-            lng_id_tmp = lng_header - strlen_of_BE;
+            int lng_id = lng_header - strlen_of_BE;
             if( str[ start_block + lng_header ] == '-' ) ++lng_header;
-            memcpy( tmpid, str + start_block + strlen_of_BE, lng_id_tmp );
-            tmpid[ lng_id_tmp ] = '\0';
 
             // リンク文字作成
+            if( ! header->headinfo->block[ BLOCK_ID_NAME ] )
+                header->headinfo->block[ BLOCK_ID_NAME ] = create_node_block();
+            else
+                create_node_space( NODE_SP, COLOR_NONE );
+
             memcpy( tmplink, PROTO_BE, sizeof( PROTO_BE ) );
-            memcpy( tmplink + sizeof( PROTO_BE ) -1, tmpid, lng_id_tmp + 1 );
+            memcpy( tmplink + sizeof( PROTO_BE ) - 1, str + start_block + strlen_of_BE, lng_id );
+            lng_link_tmp = sizeof( PROTO_BE ) - 1 + lng_id;
+            tmplink[ lng_link_tmp ] = '\0';
 
             // リンク作成
-            create_node_link( "?", 1, tmplink, strlen( tmplink ), COLOR_CHAR, false );
-            create_node_ntext( str + start_block + lng_header, lng_block - lng_header, COLOR_CHAR);
+            create_node_link( str + start_block + lng_header, lng_block - lng_header,
+                    tmplink, lng_link_tmp, COLOR_CHAR, COLOR_NONE, false );
 
             // 次のブロックへ移動
             start = start_block + lng_block;
@@ -1912,18 +1992,25 @@
                  && str[ start_block + 2 ] == ' ' ){
 
             // フラッシュ
-            if( lng_text ) create_node_ntext( str + start, lng_text, COLOR_CHAR );
+            if( lng_text ){
+                parse_html( str + start, lng_text, COLOR_CHAR, digitlink, bold );
+            }
 
             // </a>までブロックの長さを伸ばす
-            while( start_block + lng_block < lng
+            while( start_block + lng_block < lng_date
                    && ! ( ( str[ start_block + lng_block -1 ] == 'a' || str[ start_block + lng_block -1 ] == 'A' )
                           && str[ start_block + lng_block ] == '>' ) ) ++lng_block;
             ++lng_block;
 
+            // リンク作成
+            if( ! header->headinfo->block[ BLOCK_ID_NAME ] )
+                header->headinfo->block[ BLOCK_ID_NAME ] = create_node_block();
+            else
+                create_node_space( NODE_SP, COLOR_NONE );
+
             const bool digitlink = false;
             const bool bold = false;
-            const bool ahref = true;
-            parse_html( str + start_block, lng_block, COLOR_CHAR, digitlink, bold, ahref );
+            parse_html( str + start_block, lng_block, COLOR_CHAR, digitlink, bold );
 
             // 次のブロックへ移動
             start = start_block + lng_block;
@@ -1931,11 +2018,35 @@
         }
 
         // テキスト(日付含む)
-        else lng_text += lng_block;
+        else{
+            bool no_date = true;
+            for( int i = start_block; i < start_block + lng_block; ++i )
+                if( (str[ i ] >= '0' && str[ i ] <= '9') || str[ i ] == ':' ) no_date = false;
+
+            // 日付や時刻を含まないまたは1文字(IDの末尾だけ)はIDノードにする
+            if( ( no_date || lng_block == 1 ) && ! header->headinfo->block[ BLOCK_ID_NAME ] ){
+                if( lng_text ){
+                    // フラッシュ
+                    parse_html( str + start, lng_text, COLOR_CHAR, digitlink, bold );
+                    lng_text = 0;
+                }
+
+                // IDノード作成
+                header->headinfo->block[ BLOCK_ID_NAME ] = create_node_block();
+
+                // 次のブロックへ移動
+                start = start_block;
+            }
+
+            lng_text += lng_block;
+        }
     }
 
-    // フラッシュ
-    if( lng_text ) create_node_ntext( str + start, lng_text, COLOR_CHAR );
+    if( lng_text ){
+        // 末端の空白を削ってフラッシュ
+        while( lng_text > 0 && str[ start + lng_text - 1 ] == ' ' ) --lng_text;
+        parse_html( str + start, lng_text, COLOR_CHAR, digitlink, bold );
+    }
 }
 
 
@@ -1947,8 +2058,7 @@
 //
 // bold : ボールド表示
 //
-// ahref : <a href=~></a> からリンクノードを作成する
-// (例) parse_html( "<a href=\"hoge.com\">hoge</a>", 27, COLOR_CHAR, false, false );
+// (例) parse_html( "<a href=\"hoge.com\">hoge</a>", 27, COLOR_CHAR, false );
 //
 // (パッチ)
 //
@@ -1956,12 +2066,18 @@
 // Thanks to 「パッチ投稿スレ」の28氏
 // http://jd4linux.sourceforge.jp/cgi-bin/bbs/test/read.cgi/support/1151836078/28
 //
-void NodeTreeBase::parse_html( const char* str, const int lng, const int color_text,
-                               bool digitlink, const bool bold, const bool ahref )
+void NodeTreeBase::parse_html( const char* str, const int lng, const int color_text, bool digitlink, const bool bold )
 {
     const char* pos = str;
     const char* pos_end = str + lng;
     int lng_text = 0;
+    int fgcolor = color_text;
+    int fgcolor_bak = color_text;
+    int bgcolor = COLOR_NONE;
+    int bgcolor_bak = COLOR_NONE;
+    bool in_bold = bold;
+    std::vector< std::string >& colors = CORE::get_css_manager()->get_colors();
+    char tmplink[ LNG_LINK ];
     
     if( *pos == ' ' ){
 
@@ -1970,17 +2086,39 @@
         // 連続半角空白
         if( *pos == ' ' ){
 
-            while( *pos == ' ' ) m_parsed_text[ lng_text++ ] = *(pos++);
-            create_node_multispace( m_parsed_text, lng_text ); lng_text = 0;
+            while( *pos == ' ' ) m_parsed_text[ lng_text++ ] = *pos++;
+            create_node_multispace( m_parsed_text, lng_text, bgcolor ); lng_text = 0;
         }
     }
    
-    for( ; pos < pos_end; ++pos, digitlink = false ){
+    for( ; pos < pos_end; digitlink = false ){
+
+        ///////////////////////
+        // 半角空白, LF(0x0A), CR(0x0D)
+        if( *pos == ' ' || *pos == 10 || *pos == 13 ){
+
+            // フラッシュしてから半角空白ノードを作る
+            create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+
+            ++pos;
+            if( pos < pos_end ) create_node_space( NODE_SP, bgcolor );
+
+create_multispace:
+            // 連続スペースノードを作成
+            if( *pos == ' ' ){
+                while( *pos == ' ' ) m_parsed_text[ lng_text++ ] = *pos++;
+                if( pos < pos_end ){
+                    create_node_multispace( m_parsed_text, lng_text, bgcolor ); lng_text = 0;
+                }
+            }
+
+            continue;
+        }
 
 
         ///////////////////////
         // HTMLタグ
-        if( *pos == '<' ){ 
+        else if( *pos == '<' ){ 
 
             bool br = false;
 
@@ -1989,12 +2127,11 @@
                 && ( *( pos + 2 ) == 'r' || *( pos + 2 ) == 'R' )
                 ) br = true;
 
-            //  ahref == true かつ <a href=~></a>
-            else if( ahref &&
-                     ( *( pos + 1 ) == 'a' || *( pos + 1 ) == 'A' ) && *( pos + 2 ) == ' ' ){
+            //  <a href=~></a>
+            else if( ( *( pos + 1 ) == 'a' || *( pos + 1 ) == 'A' ) && *( pos + 2 ) == ' ' ){
 
                 // フラッシュ
-                create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
 
                 while( pos < pos_end && *pos != '=' ) ++pos;
                 ++pos;
@@ -2021,9 +2158,7 @@
                 const char* pos_str_start = pos;
                 int lng_str = 0;
 
-                bool exec_decode = false;
-                while( pos < pos_end && *pos != '<' ){
-                    if( *pos == '&' ) exec_decode = true;
+                while( pos < pos_end && ( *pos != '<' || pos[ 1 ] != '/' || ( pos[ 2 ] != 'a' && pos[ 2 ] != 'A' ) || pos[ 3 ] != '>' ) ){
                     ++pos;
                     ++lng_str;
                 }
@@ -2035,30 +2170,113 @@
 
                 if( lng_link && lng_str ){
 
-                    // 特殊文字デコード
-                    if( exec_decode ){
+                    // BE link
+                    if( memcmp( pos_link_start, "javascript:be(", 14 ) == 0 ){
+                        memcpy( tmplink, PROTO_BE, sizeof( PROTO_BE ) );
+                        memcpy( tmplink + sizeof( PROTO_BE ) -1, pos_link_start +14, lng_link -14 -2 );
+                        create_node_link( pos_str_start, lng_str, tmplink, strlen( tmplink ), fgcolor, bgcolor, in_bold );
+                    }
+                    else{
+
+                        if( *pos_link_start == '/' || ! memcmp( pos_link_start, "../", 3 ) ){
+                            size_t link_offset = ( *pos_link_start == '/' ) ? 0 : 2;
 
-                        for( int pos_tmp = 0; pos_tmp < lng_str; ++ pos_tmp ){
-                            int n_in = 0;
-                            int n_out = 0;
-                            const int ret_decode = DBTREE::decode_char( pos_str_start + pos_tmp, n_in, m_parsed_text + lng_text, n_out, false );
-                            if( ret_decode != NODE_NONE ){
-                                lng_text += n_out;
-                                pos_tmp += n_in;
-                                pos_tmp--;
+                            // アンカーの判定
+                            size_t pos_root = m_url_readcgi.find_first_of( '/', 8 ); // httpsでも8文字目でOK
+                            if( pos_root == std::string::npos ||
+                                    ! m_url_readcgi.compare( pos_root, m_url_readcgi.length() - pos_root, pos_link_start + link_offset, m_url_readcgi.length() - pos_root ) ){
+
+                                // アンカーは後で処理するのでここは抜ける
+                                pos = pos_str_start;
+                                lng_str = 0;
+                            }
+                            else if( pos_root + lng_link >= LNG_LINK ){
+                                // XXX リンクが長すぎる
+                                pos = pos_str_start;
+                                lng_str = 0;
+                            }
+                            else{
+                                // 相対リンクから完全なURLを作る
+                                m_url_readcgi.copy( tmplink, pos_root );
+                                memcpy( tmplink + pos_root, pos_link_start + link_offset, lng_link - link_offset );
+                                lng_link += pos_root - link_offset;
                             }
-                            else m_parsed_text[ lng_text++ ] = *( pos_str_start + pos_tmp );
                         }
+                        else{
+                            memcpy( tmplink, pos_link_start, lng_link );
+                            tmplink[ lng_link ] = '\0';
+
+                            while( remove_imenu( tmplink ) ); // ime.nuなどの除去
+                        }
+
+                        if( lng_str ){
+
+                            for( int pos_tmp = 0; pos_tmp < lng_str; ){
+                                const char ch = pos_str_start[ pos_tmp ];
+
+                                if( ch == '\t' || ch == '\n' || ch == '\r' || ch == ' ' ){
+                                    // 空白の連続は削る
+                                    if( lng_text && m_parsed_text[ lng_text - 1 ] != ' ' ){
+                                        m_parsed_text[ lng_text++ ] = ' ';
+                                    }
+                                    ++pos_tmp;
+                                    continue;
+                                }
+                                else if( ch == '<' ){
+                                    // タグは取り除く
+                                    while( pos_tmp < lng_str && pos_str_start[ pos_tmp ] != '>' ) ++pos_tmp;
+                                    if( pos_str_start[ pos_tmp ] == '>' ) ++pos_tmp;
+                                    continue;
+                                }
+                                else if( ch != '&' ){
+                                    // 表示用文字列にコピー
+                                    m_parsed_text[ lng_text++ ] = pos_str_start[ pos_tmp++ ];
+                                    continue;
+                                }
+
+                                // 特殊文字デコード
+                                int n_in = 0;
+                                int n_out = 0;
+                                const int ret_decode = decode_char( pos_str_start + pos_tmp, n_in, m_parsed_text + lng_text, n_out, false );
+                                switch( ret_decode ){
+
+                                case NODE_HTAB:
+                                case NODE_SP:
+                                    // 空白の連続は無視する
+                                    if( !lng_text || m_parsed_text[ lng_text - 1 ] != ' ' ){
+                                        m_parsed_text[ lng_text++ ] = ' ';
+                                    }
+                                    pos_tmp += n_in;
+                                    break;
+
+                                case NODE_ZWSP:
+                                case NODE_TEXT:
+                                    if( m_parsed_text[ lng_text ] == '\xc2'
+                                        && m_parsed_text[ lng_text + 1 ] == '\xa0' ){
+                                        // &nbsp; は空白に置き換える
+                                        m_parsed_text[ lng_text++ ] = ' ';
+                                    }
+                                    else lng_text += n_out;
+                                    pos_tmp += n_in;
+                                    break;
+
+                                case NODE_NONE:
+                                default:
+                                    m_parsed_text[ lng_text++ ] = pos_str_start[ pos_tmp++ ];
+                                }
+                            }
 #ifdef _DEBUG
-                        m_parsed_text[ lng_text ] = '\0';
-                        std::cout << m_parsed_text << std::endl;
+                            m_parsed_text[ lng_text ] = '\0';
+                            std::cout << m_parsed_text << std::endl;
 #endif
-                        pos_str_start = m_parsed_text;
-                        lng_str = lng_text;
-                        lng_text = 0;
-                    }
+                            pos_str_start = m_parsed_text;
+                            lng_str = lng_text;
+                            lng_text = 0;
+                        }
 
-                    create_node_link( pos_str_start, lng_str , pos_link_start, lng_link, COLOR_CHAR_LINK, false );
+                        // アンカーの時はlng_strが0でリンクを作らない
+                        if( lng_str ) create_node_link( pos_str_start, lng_str, tmplink, lng_link, COLOR_CHAR_LINK, bgcolor, in_bold );
+                    }
                 }
             }
 
@@ -2093,6 +2311,12 @@
                     && *( pos + 1 ) == '/'
                     )
 
+                // <ul>
+                || (
+                    ( *( pos + 1 ) == 'u' || *( pos + 1 ) == 'U' )
+                    && ( *( pos + 2 ) == 'l' || *( pos + 2 ) == 'L' )
+                    )
+
                 // </ul>
                 || (
                     ( *( pos + 2 ) == 'u' || *( pos + 2 ) == 'U' )
@@ -2119,17 +2343,16 @@
 
                 ) br = true;
 
-            // <li>は・にする
-            else if( ( *( pos + 1 ) == 'l' || *( pos + 2 ) == 'L' )
-                     && ( *( pos + 2 ) == 'i' || *( pos + 3 ) == 'I' )
+            // <li>はBULLET (•)にする
+            else if( ( *( pos + 1 ) == 'l' || *( pos + 1 ) == 'L' )
+                     && ( *( pos + 2 ) == 'i' || *( pos + 2 ) == 'I' )
                 ){
 
                 pos += 4;
 
-                int n_in = 0;
-                int n_out = 0;
-                DBTREE::decode_char( "&#12539;", n_in, m_parsed_text + lng_text, n_out, false );
-                lng_text += n_out;
+                char const li_str [] = "\xe2\x80\xa2"; // •
+                memcpy( m_parsed_text + lng_text, li_str, sizeof( li_str ) );
+                lng_text += sizeof( li_str ) - 1;
             }
 
             // 水平線 <HR>
@@ -2137,7 +2360,7 @@
                      && ( *( pos + 2 ) == 'r' || *( pos + 2 ) == 'R' ) ){
 
                 // フラッシュ
-                create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
 
                 // 水平線ノード作成
                 create_node_hr();
@@ -2145,11 +2368,165 @@
                 pos += 4;
             }
 
-            // その他のタグは無視。タグを取り除いて中身だけを見る
+            // ボールド <B>
+            else if( ( *( pos + 1 ) == 'b' || *( pos + 1 ) == 'B' ) && *( pos + 2 ) == '>' ){
+
+                // フラッシュ
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+                in_bold = true;
+                pos += 3;
+            }
+
+            // </B>
+            else if( *( pos + 1 ) == '/' && ( *( pos + 2 ) == 'b' || *( pos + 2 ) == 'B' )
+                     && *( pos + 3 ) == '>' ){
+
+                // フラッシュ
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+                in_bold = bold;
+                pos += 4;
+            }
+
+            // 閉じるタグの時は文字色を戻す
+            else if( *( pos + 1 ) == '/' ){
+                // フラッシュ
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+
+                fgcolor = fgcolor_bak;
+                fgcolor_bak = color_text;
+                bgcolor = bgcolor_bak;
+                bgcolor_bak = COLOR_NONE;
+
+                while( pos < pos_end && *pos != '>' ) ++pos;
+                ++pos;
+            }
+
+            // その他のタグはタグを取り除いて中身だけを見る
             else {
 
                 // フラッシュ
-                create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+
+                // <span
+                if(( *( pos + 1 ) == 's' || *( pos + 1 ) == 'S' )
+                        && ( *( pos + 2 ) == 'p' || *( pos + 2 ) == 'P' )
+                        && ( *( pos + 3 ) == 'a' || *( pos + 3 ) == 'A' )
+                        && ( *( pos + 4 ) == 'n' || *( pos + 4 ) == 'N' )
+                        ){
+                    pos += 5;
+
+                    // class属性を取り出す
+                    std::string classname;
+                    if( strncasecmp( pos, " class=\"", 8 ) == 0 ){
+                        pos += 8;
+                        const char* pos_name = pos;
+                        while( *pos != '"' && *pos != '\0' ) ++pos;
+                        classname = std::string( pos_name, pos - pos_name );
+                    }
+
+                    // 文字色を保存
+                    fgcolor_bak = fgcolor;
+                    bgcolor_bak = bgcolor;
+
+                    if( ! classname.empty() && classname != "name" ){
+
+                        CORE::Css_Manager* mgr = CORE::get_css_manager();
+                        const int classid = mgr->get_classid( classname );
+                        if( classid != -1 ){
+                            CORE::CSS_PROPERTY css = mgr->get_property( classid );
+                            if( css.color != -1 ) fgcolor = css.color;
+                            if( css.bg_color != -1 ) bgcolor = css.bg_color;
+                        }
+                        else{
+                            fgcolor = COLOR_CHAR_NAME_B;
+                        }
+                    }
+                }
+
+                // <mark
+                else if(( *( pos + 1 ) == 'm' || *( pos + 1 ) == 'M' )
+                        && ( *( pos + 2 ) == 'a' || *( pos + 2 ) == 'A' )
+                        && ( *( pos + 3 ) == 'r' || *( pos + 3 ) == 'R' )
+                        && ( *( pos + 4 ) == 'k' || *( pos + 4 ) == 'K' )
+                        ){
+
+                    CORE::Css_Manager* mgr = CORE::get_css_manager();
+                    CORE::CSS_PROPERTY css = mgr->get_property( mgr->get_classid( "mark" ) );
+                    fgcolor_bak = fgcolor;
+                    bgcolor_bak = bgcolor;
+                    if( css.color != -1 ) fgcolor = css.color;
+                    if( css.bg_color != -1 ) bgcolor = css.bg_color;
+                }
+
+                if( CONFIG::get_use_color_html() ){
+                    // タグで指定された色を使う場合
+
+                    while( pos < pos_end && *pos != '>' ){
+                        bool background = false;
+
+                        while( pos < pos_end && *pos != '>' && *pos != ' '
+                                && *pos != '"' && *pos != '\'' ) ++pos;
+
+                        if( *pos == '>' ) break;
+
+                        else if( ( *pos == ' ' || *pos == '"' || *pos == '\'' )
+                                && ! memcmp( pos + 1, "color", 5 ) ) pos += 6;
+
+                        else if( ( *pos == ' ' || *pos == '"' || *pos == '\'' )
+                                && ! memcmp( pos + 1, "fgcolor", 7 ) ) pos += 8;
+
+                        else if( ( *pos == ' ' || *pos == '"' || *pos == '\'' )
+                                && ! memcmp( pos + 1, "foreground", 10 ) ) pos += 11;
+
+                        else if( ( *pos == ' ' || *pos == '"' || *pos == '\'' )
+                                && ! memcmp( pos + 1, "bgcolor", 7 ) ){ pos += 8; background = true; }
+
+                        else if( ( *pos == ' ' || *pos == '"' || *pos == '\'' )
+                                && ! memcmp( pos + 1, "background-color", 16 ) ){ pos += 17; background = true; }
+
+                        else if( ( *pos == ' ' || *pos == '"' || *pos == '\'' )
+                                && ! memcmp( pos + 1, "background", 10 ) ){ pos += 11; background = true; }
+
+                        else{ ++pos; continue; }
+
+                        if( *pos == '=' || *pos == ':' ) ++pos;
+                        if( *pos == ' ' || *pos == '"' ) ++pos;
+
+                        const char* st = pos;
+                        while( pos < pos_end && *pos != '>' && *pos != ' ' && *pos != ';' && *pos != '\'' && *pos != '"' ) ++pos;
+
+                        std::string col( st, pos - st );
+                        if( col == "transparent" ){
+                            // 透明の場合は基本の背景色(でよいか?)
+                            if( background ) bgcolor = COLOR_BACK;
+                            else fgcolor = COLOR_BACK;
+                            continue;
+                        }
+
+                        const std::string col_str = MISC::htmlcolor_to_str( col ) ;
+                        if( col_str.empty() ){
+                            MISC::ERRMSG( "unhandled color : " + col );
+                            continue;
+                        }
+
+                        std::vector< std::string >::const_iterator it = colors.begin();
+                        for( ; it < colors.end(); ++it )
+                            if( ! (*it).compare( col_str ) ) break;
+
+                        int num_color;
+                        if( it == colors.end() ){
+                            colors.push_back( col_str );
+                            num_color = colors.size() - 1;
+                        }
+                        else{
+                            num_color = it - colors.begin();
+                        }
+
+                        // 指定された文字色に変更する
+                        if( background ) bgcolor = num_color + USRCOLOR_BASE;
+                        else fgcolor = num_color + USRCOLOR_BASE;
+                    }
+                }
                 
                 while( pos < pos_end && *pos != '>' ) ++pos;
                 ++pos;
@@ -2159,13 +2536,13 @@
             if( br ){
 
                 // フラッシュ
-                if( *( pos - 1 ) == ' ' ) --lng_text; // 改行前の空白を取り除く
-                create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
+                create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
 
                 // 改行ノード作成
                 create_node_br();
 
-                while( *pos != '>' ) ++pos; ++pos;
+                while( *pos != '>' ) ++pos;
+                ++pos;
 
                 if( *pos == ' ' ){
 
@@ -2175,13 +2552,11 @@
                     if( *pos == ' ' ){
 
                         while( *pos == ' ' ) m_parsed_text[ lng_text++ ] = *(pos++);
-                        create_node_multispace( m_parsed_text, lng_text ); lng_text = 0;
+                        create_node_multispace( m_parsed_text, lng_text, bgcolor ); lng_text = 0;
                     }
                 }
             }
 
-            // forのところで++されるので--しておく
-            --pos;
             continue;
         }
 
@@ -2191,22 +2566,21 @@
         int n_in = 0;
         int n_out = 0;
         char tmpstr[ LNG_LINK +16 ]; // 画面に表示する文字列
-        char tmplink[ LNG_LINK +16 ]; // 編集したリンク文字列
-        int lng_str = 0, lng_link = strlen( PROTO_ANCHORE );
+        size_t lng_str = 0, lng_link = strlen( PROTO_ANCHORE );
         ANCINFO ancinfo[ MAX_ANCINFO ];
-        int lng_anc = 0;
+        size_t lng_anc = 0;
 
         int mode = 0;
         if( digitlink ) mode = 2;
 
-        if( check_anchor( mode , pos, n_in, tmpstr + lng_str, tmplink + lng_link, LNG_LINK - lng_link, ancinfo + lng_anc ) ){
+        if( check_anchor( mode , pos, n_in, tmpstr, tmplink + lng_link, LNG_LINK - lng_link, ancinfo ) ){
 
             // フラッシュしてからアンカーノードをつくる
-            create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
+            create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
 
             memcpy( tmplink, PROTO_ANCHORE, strlen( PROTO_ANCHORE ) );
-            lng_str += strlen( tmpstr ) - lng_str;
-            lng_link += strlen( tmplink ) - lng_link;
+            lng_str = strlen( tmpstr );
+            lng_link = strlen( tmplink );
             ++lng_anc;
             pos += n_in; 
 
@@ -2215,16 +2589,14 @@
             while( check_anchor( mode, pos, n_in, tmpstr + lng_str, tmplink + lng_link ,
                                  LNG_LINK - lng_link, ancinfo + lng_anc ) ){
 
-                lng_str += strlen( tmpstr ) - lng_str;
-                lng_link += strlen( tmplink ) - lng_link;
+                lng_str = strlen( tmpstr );
+                lng_link = strlen( tmplink );
                 ++lng_anc;
                 pos += n_in; 
             }
 
             create_node_anc( tmpstr, lng_str, tmplink, lng_link, COLOR_CHAR_LINK, bold, ancinfo, lng_anc );
 
-            // forのところで++されるので--しておく
-            --pos;
             continue;
         }
 
@@ -2236,79 +2608,62 @@
 
         ///////////////////////
         // リンク(http)のチェック
-        char tmpreplace[ LNG_LINK +16 ]; // Urlreplaceで変換した後のリンク文字列
-        int lng_replace = 0;
-        int linktype = check_link( pos, (int)( pos_end - pos ), n_in, tmplink, LNG_LINK );
+        n_in = pos_end - pos;
+        lng_str = lng_link = LNG_LINK;
+        int linktype = check_link( pos, n_in, (char*)tmpstr, lng_str, (char*)tmplink, lng_link );
+
         if( linktype != MISC::SCHEME_NONE ){
             // リンクノードで実際にアクセスするURLの変換
             while( remove_imenu( tmplink ) ); // ime.nuなどの除去
-            lng_link = convert_amp( tmplink, strlen( tmplink ) ); // &amp; → &
+            lng_link = strlen( tmplink );
 
             // Urlreplaceによる正規表現変換
-            std::string tmpurl( tmplink, lng_link );
-            if( CORE::get_urlreplace_manager()->exec( tmpurl ) == false ){
-                // 変換されてない
-                lng_replace = lng_link;
-                memcpy( tmpreplace, tmplink, lng_replace +1 );
+            std::string tmpreplace( tmplink, lng_link );
+            if( CORE::get_urlreplace_manager()->exec( tmpreplace ) ){
+                int delim_pos = 0;
 
-            } else {
-                if( tmpurl.size() > LNG_LINK ){
+                if( tmpreplace.length() >= LNG_LINK ){
+                    // 変換後のURLが長すぎるので、元のURLのままにする
                     MISC::ERRMSG( std::string( "too long replaced url : " ) + tmplink );
+                    tmpreplace.assign( tmplink, lng_link );
 
-                    // 変換後のURLが長すぎるので、元のURLのままにする
-                    lng_replace = lng_link;
-                    memcpy( tmpreplace, tmplink, lng_replace +1 );
-                } else {
-                    // 正常に変換された
-                    lng_replace = tmpurl.size();
-                    memcpy( tmpreplace, tmpurl.c_str(), lng_replace +1 );
-
-                    // 正規表現変換の結果、スキームだけの簡易チェックをする
-                    int delim_pos = 0;
-                    if( MISC::SCHEME_NONE == MISC::is_url_scheme( tmpreplace, &delim_pos ) ){
-                        // スキーム http:// が消えていた
-                        linktype = MISC::SCHEME_NONE;
-                    }
+                // 正常に変換された結果、スキームだけの簡易チェックをする
+                } else if( MISC::SCHEME_NONE == MISC::is_url_scheme( tmpreplace.c_str(), &delim_pos ) ){
+                    // プロトコルスキームが消えていた
+                    memcpy( m_parsed_text + lng_text, tmpstr, lng_str );
+                    lng_text += lng_str;
+                    pos += n_in;
+                    continue;
                 }
             }
-        }
-        // リンクノードか再チェック
-        if( linktype != MISC::SCHEME_NONE ){
-            // フラッシュしてからリンクノードつくる
-            create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
 
-            // リンクノードの表示テキスト
-            memcpy( tmpstr, pos, n_in );
-            tmpstr[ n_in ] = '\0';
-            lng_str = convert_amp( tmpstr, n_in ); // &amp; → &
+            // フラッシュしてからリンクノードつくる
+            create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
 
             // ssspアイコン
             if( linktype == MISC::SCHEME_SSSP ){
-                create_node_sssp( tmpreplace, lng_replace );
+                create_node_sssp( tmpreplace.c_str(), tmpreplace.length() );
             }
 
             else {
                 // Urlreplaceによる画像コントロールを取得する
-                int imgctrl = CORE::get_urlreplace_manager()->get_imgctrl( std::string( tmpreplace, lng_replace ) );
+                int imgctrl = CORE::get_urlreplace_manager()->get_imgctrl( tmpreplace );
 
                 // youtubeなどのサムネイル画像リンク
                 if( imgctrl & CORE::IMGCTRL_THUMBNAIL ){
-                    create_node_thumbnail( tmpstr, lng_str, tmplink , lng_link, tmpreplace, lng_replace, COLOR_CHAR_LINK, bold );
+                    create_node_thumbnail( tmpstr, lng_str, tmplink , lng_link, tmpreplace.c_str(), tmpreplace.length(), COLOR_CHAR_LINK, bold );
                 }
 
                 // 画像リンク
-                else if( DBIMG::get_type_ext( tmpreplace, lng_replace ) != DBIMG::T_UNKNOWN ){
-                    create_node_img( tmpstr, lng_str, tmpreplace , lng_replace, COLOR_IMG_NOCACHE, bold );
+                else if( DBIMG::get_type_ext( tmpreplace.c_str(), tmpreplace.length() ) != DBIMG::T_UNKNOWN ){
+                    create_node_img( tmpstr, lng_str, tmpreplace.c_str(), tmpreplace.length(), COLOR_IMG_NOCACHE, bold );
                 }
     
                 // 一般リンク
-                else create_node_link( tmpstr, lng_str, tmpreplace , lng_replace, COLOR_CHAR_LINK, bold );
+                else create_node_link( tmpstr, lng_str, tmpreplace.c_str(), tmpreplace.length(), COLOR_CHAR_LINK, bgcolor, bold );
             }
 
             pos += n_in;
-
-            // forのところで++されるので--しておく
-            --pos;
             continue;
         }
 
@@ -2316,65 +2671,85 @@
         // 特殊文字デコード
         if( *pos == '&' ){
 
-            const int ret_decode = DBTREE::decode_char( pos, n_in, m_parsed_text + lng_text, n_out, false );
+            const int ret_decode = decode_char( pos, n_in, m_parsed_text + lng_text, n_out, false );
 
             if( ret_decode != NODE_NONE ){
 
                 // 文字以外の空白ノードならフラッシュして空白ノード追加
                 if( ret_decode != NODE_TEXT ){
-                    create_node_ntext( m_parsed_text, lng_text, color_text, bold );
+                    create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold );
                     lng_text = 0;
-                    create_node_space( ret_decode );
+                    create_node_space( ret_decode, bgcolor );
+                }
+                else if( m_parsed_text[ lng_text ] == '\xc2'
+                        && m_parsed_text[ lng_text + 1 ] == '\xa0' ){
+                    // &nbsp; は空白に置き換える
+                    m_parsed_text[ lng_text++ ] = ' ';
                 }
                 else lng_text += n_out;
 
                 pos += n_in;
-
-                // forのところで++されるので--しておく
-                --pos;
                 continue;
             }
         }
 
         ///////////////////////
         // 水平タブ(0x09)
-        if( *pos == 0x09 ){
+        else if( *pos == '\t' ){
 
             // フラッシュしてからタブノードをつくる
-            create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
-            create_node_htab();
-            continue;
+            create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+            create_node_space( NODE_HTAB, bgcolor );
+            ++pos;
+
+            goto create_multispace;
         }
 
         ///////////////////////
-        // LF(0x0A), CR(0x0D)
-        if( *pos == 0x0A || *pos == 0x0D ){
+        // フォームフィード(0x0C)
+        else if( *pos == '\f' ){
+
+            // フラッシュしてからZWSPノードをつくる
+            create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold ); lng_text = 0;
+            create_node_space( NODE_ZWSP, bgcolor );
+            ++pos;
 
-            // 無視する
             continue;
         }
 
         ///////////////////////
-        // 連続半角空白
-        if( *pos == ' ' && *( pos + 1 ) == ' ' ){
-
-            m_parsed_text[ lng_text++ ] = *(pos++);
-
-            // フラッシュしてから連続半角ノードを作る
-            create_node_ntext( m_parsed_text, lng_text, color_text, bold ); lng_text = 0;
-
-            while( *pos == ' ' ) m_parsed_text[ lng_text++ ] = *(pos++);
-            create_node_multispace( m_parsed_text, lng_text ); lng_text = 0;
+        // NBSP(0xC2 0xA0)
+        else if( *pos == '\xc2' && *(pos + 1) == '\xa0' ){
+            // 空白に置き換える
+            m_parsed_text[ lng_text++ ] = ' ';
+            pos += 2;
 
-            // forのところで++されるので--しておく
-            --pos;
             continue;
         }
 
-        m_parsed_text[ lng_text++ ] = *pos;
+        ///////////////////////
+        // その他のASCIIおよびマルチバイト文字
+        switch( MISC::utf8bytes( pos ) ){
+        case 4: m_parsed_text[ lng_text++ ] = *pos++;
+        case 3: m_parsed_text[ lng_text++ ] = *pos++;
+        case 2: m_parsed_text[ lng_text++ ] = *pos++;
+        case 1: m_parsed_text[ lng_text++ ] = *pos++;
+                break;
+        default:
+            // Iconvでエンコード変換済みなので不正な文字が
+            // ここで検出されるのは何かがおかしい
+            MISC::ERRMSG( "invalid char = " + MISC::itostr( (unsigned char) *pos ) );
+
+            // REPLACEMENT CHARACTERに置き換える
+            ++pos;
+            m_parsed_text[ lng_text++ ] = 0xef;
+            m_parsed_text[ lng_text++ ] = 0xbf;
+            m_parsed_text[ lng_text++ ] = 0xbd;
+            break;
+        }
     }
 
-    create_node_ntext( m_parsed_text, lng_text, color_text, bold );
+    create_node_ntext( m_parsed_text, lng_text, fgcolor, bgcolor, in_bold );
 }
 
 
@@ -2484,7 +2859,7 @@
         else if( *pos == '&' && *( pos + 1 ) == '#' && ( lng_num = MISC::spchar_number_ln( pos, offset_num ) ) != -1 ){
 
             const int num = MISC::decode_spchar_number( pos, offset_num, lng_num );
-            const int n_out = MISC::ucs2toutf8( num, pos_write );
+            const int n_out = MISC::cptoutf8( num, pos_write );
             pos_write += n_out;
             pos += offset_num + lng_num;
 
@@ -2666,124 +3041,134 @@
 //
 // 入力
 // str_in : 入力文字列の先頭アドレス
-// lng_str : str_inのバッファサイズ
+// lng_in : str_inのバッファサイズ
+// lng_text : str_textのバッファサイズ
 // lng_link : str_linkのバッファサイズ
-// linktype : is_url_scheme()のリタンコード
-// delim_pos : is_url_scheme()で得たスキーム文字列の長さ
 //
 // 出力
-// n_in : str_in から何バイト読み取ったか
+// lng_in : str_in から何バイト読み取ったか
+// str_text : リンクの表示文字列
+// lng_text : str_textのサイズ
 // str_link : リンクの文字列
+// lng_link : str_linkのサイズ
 //
 // 戻り値 : リンクのタイプ(例えばSCHEME_HTTPなど)
 //
 // 注意 : MISC::is_url_scheme() と MISC::is_url_char() の仕様に合わせる事
 //
-const int NodeTreeBase::check_link_impl( const char* str_in, const int lng_in, int& n_in, char* str_link, const int lng_link, const int linktype, const int delim_pos )
+const int NodeTreeBase::check_link( const char* str_in, int& lng_in, char* str_text, size_t& lng_text, char* str_link, size_t& lng_link )
 {
+    int delim_pos = 0;
+    int linktype = MISC::is_url_scheme( str_in, &delim_pos );
+    if( linktype == MISC::SCHEME_NONE ) return linktype;
+
     // CONFIG::get_loose_url() == true の時はRFCで規定されていない文字も含める
     const bool loose_url = CONFIG::get_loose_url();
 
-    // リンクの長さを取得
-    n_in = delim_pos;
-    int n_in_tmp, n_out_tmp;
-    char buf[16];
-
-    while( n_in < lng_in ){
-
-        // URLとして扱う文字かどうか
-        if ( MISC::is_url_char( str_in + n_in, loose_url ) == false ) break;
-
-        // HTML特殊文字( &〜; )
-        if ( *( str_in + n_in ) == '&' &&
-             DBTREE::decode_char( str_in + n_in, n_in_tmp, buf, n_out_tmp, false ) != DBTREE::NODE_NONE ){
-
-             // デコード結果が"&(&amp;)"でないもの
-             if( n_out_tmp != 1 || buf[0] != '&' ) break;
-        }
-
-        n_in++;
-    }
-
-    // URLとして短かすぎる場合は除外する( 最短ドメイン名の例 "1.cc" )
-    if( n_in - delim_pos < 4 ) return MISC::SCHEME_NONE;
-
-    // URL出力バッファより長いときも除外する( 一般に256バイトを超えるとキャッシュをファイル名として扱えなくなる )
-    if( lng_link <= n_in ) return MISC::SCHEME_NONE;
-
-    char *pos = str_link;
+    const char *pos_in = str_in;
+    char *pos_text = str_text;
+    char *pos_link = str_link;
+    const char* pos_in_end = str_in + lng_in;
+    const char* pos_text_end = str_text + lng_text;
+    const char* pos_link_end = str_link + lng_link;
 
     // URLスキームを修正
-    int str_pos = 0;
     switch( linktype ){
+        case MISC::SCHEME_SSSP:
+        case MISC::SCHEME_HTTP: *pos_text++ = *pos_in++;
+        case MISC::SCHEME_TTP:  *pos_text++ = *pos_in++;
+        case MISC::SCHEME_TP:   *pos_text++ = *pos_in++;
+    }
+
+    // str_inの文字列をdelim_posまでコピー
+    *pos_link++ = 'h'; *pos_link++ = 't'; *pos_link++ = 't';
+    for( ; pos_in < str_in + delim_pos; ++pos_in ) *pos_text++ = *pos_link++ = *pos_in;
+
+    // str_inの残り文字列をstr_linkにコピー
+    while( pos_in < pos_in_end ){
+        int n_in_tmp, n_out_tmp;
+        char ch = *pos_in;
+
+        // 文字参照を変換
+        if( ch == '&' && decode_char( pos_in, n_in_tmp, pos_link, n_out_tmp, false ) != NODE_NONE ){
+            // URLとして扱う文字かどうか
+            if( n_out_tmp == 1 && MISC::is_url_char( pos_link, loose_url ) ){
+                ch = *pos_text++ = *pos_link++;
+                pos_in += n_in_tmp;
+            }
+            else{
+                break;
+            }
+        }
 
-        // ttp -> http
-        case MISC::SCHEME_TTP:
-
-            if( n_in + 1 >= lng_link ) return MISC::SCHEME_NONE;
-
-            *pos = 'h';
-            pos++;
+        // URLとして扱う文字かどうか
+        else if( MISC::is_url_char( pos_in, loose_url ) ){
+            *pos_text++ = *pos_link++ = ch;
+            ++pos_in;
+        }
+        else{
             break;
+        }
 
-        // tp -> http
-        case MISC::SCHEME_TP:
+        if( pos_text >= pos_text_end || pos_link >= pos_link_end ) return MISC::SCHEME_NONE;
 
-            if( n_in + 2 >= lng_link ) return MISC::SCHEME_NONE;
+        // loose_urlで含める"^"と"|"をエンコードする
+        // "[]"はダウンローダに渡す用途のためにエンコードしないでおく
+        if( loose_url ){
 
-            *pos  = 'h';
-            *(++pos) = 't';
-            pos++;
-            break;
+            if( ch == '^' ){        // '^' → "%5E"(+2Byte)
+                if( pos_link + 2 >= pos_link_end ) return MISC::SCHEME_NONE;
 
-        // sssp -> http
-        case MISC::SCHEME_SSSP:
+                *( pos_link - 1 ) = '%';
+                *pos_link++ = '5';
+                *pos_link++ = 'E';
+            }
+            else if( ch == '|' ){   // '|' → "%7C"(+2Byte)
+                if( pos_link + 2 >= pos_link_end ) return MISC::SCHEME_NONE;
 
-            *pos = 'h';
-            *(++pos) = 't';
-            *(++pos) = 't';
-            pos++;
-            str_pos = 3;
-            break;
+                *( pos_link - 1 ) = '%';
+                *pos_link++ = '7';
+                *pos_link++ = 'C';
+            }
+        }
     }
 
-    // srr_inの文字列をstr_linkにコピー
-    int i = str_pos;
-    for( ; i < n_in; i++, pos++ ){
-
-        *pos = str_in[ i ];
-
-        // loose_urlで含める"^"と"|"をエンコードする
-        // "[]"はダウンローダに渡す用途のためにエンコードしないでおく
-        if( loose_url == true ){
+    // str_linkの終端
+    *pos_text = *pos_link = '\0';
+    lng_in = pos_in - str_in;
+    lng_text = pos_text - str_text;
+    lng_link = pos_link - str_link;
 
-            if( str_in[ i ] == '^' ){
+    // パーセントコードの処理
+    if( CONFIG::get_percent_decode() && memchr( str_text, '%', lng_text ) != NULL )
+    {
+        std::string tmp = MISC::url_decode( str_text, lng_text );
 
-                // '^' → "%5E"(+2Byte)
-                if( n_in + 2 >= lng_link ) return MISC::SCHEME_NONE;
+        const CharCode charcode = MISC::judge_char_code( tmp );
 
-                *pos = '%';
-                *(++pos) = '5';
-                *(++pos) = 'E';
-            }
-            else if( str_in[ i ] == '|' ){
+        if( charcode == CHARCODE_SJIS || charcode == CHARCODE_EUCJP ||
+                charcode == CHARCODE_JIS ){
+            tmp = MISC::Iconv( tmp, charcode, CHARCODE_UTF8 );
+        }
 
-                // '|' → "%7C"(+2Byte)
-                if( n_in + 2 >= lng_link ) return MISC::SCHEME_NONE;
+        if( charcode != CHARCODE_UNKNOWN ){
+            // 改行はパーセントエンコード( 元に戻す )
+            tmp = MISC::replace_newlines_to_str( tmp, "%0A" );
 
-                *pos = '%';
-                *(++pos) = '7';
-                *(++pos) = 'C';
+            if( tmp.length() < lng_text ){
+                tmp.copy( str_text, std::string::npos );
+                lng_text = tmp.length();
             }
         }
     }
 
-    // str_linkの終端
-    *pos = '\0';
+    // URLとして短かすぎる場合は除外する( 最短ドメイン名の例 "1.cc" )
+    if( lng_in - delim_pos < 4 ) return MISC::SCHEME_NONE;
+
 
 #ifdef _DEBUG
     std::cout << str_link << std::endl
-              << "len = " << strlen( str_link ) << " lng_link = " << lng_link << " n_in = " << n_in << std::endl;
+              << " lng_link = " << lng_link << " lng_in = " << lng_in << std::endl;
 #endif
 
     return linktype;
@@ -2821,6 +3206,7 @@
                                     const std::list< std::string >& list_abone_regex,
                                     const std::vector< char >& vec_abone_res,
                                     const bool abone_transparent, const bool abone_chain, const bool abone_age,
+                                    const bool abone_default_name, const bool abone_noid,
                                     const bool abone_board, const bool abone_global )
 {
     m_list_abone_id = list_abone_id;
@@ -2829,17 +3215,40 @@
     m_list_abone_id_board = DBTREE::get_abone_list_id_board( m_url );
     m_list_abone_name_board = DBTREE::get_abone_list_name_board( m_url );
 
+    std::list< std::string > list_str;
+    const bool icase = CONFIG::get_abone_icase();
+    const bool newline = true;
+    const bool wchar = CONFIG::get_abone_wchar();
+
     // 設定ファイルには改行は"\\n"で保存されているので "\n" に変換する
     m_list_abone_word = MISC::replace_str_list( list_abone_word, "\\n", "\n" );
-    m_list_abone_regex = MISC::replace_str_list( list_abone_regex, "\\n", "\n" );
+    list_str = MISC::replace_str_list( list_abone_regex, "\\n", "\n" );
+    m_list_abone_regex.clear();
+    for( std::list< std::string >::const_iterator it = list_str.begin();
+            it != list_str.end(); it++ ){
+        m_list_abone_regex.push_back( JDLIB::RegexPattern() );
+        m_list_abone_regex.rbegin()->set( *it, icase, newline, false, wchar );
+    }
 
     m_list_abone_word_board = DBTREE::get_abone_list_word_board( m_url );
     m_list_abone_word_board = MISC::replace_str_list( m_list_abone_word_board, "\\n", "\n" );
-    m_list_abone_regex_board = DBTREE::get_abone_list_regex_board( m_url );
-    m_list_abone_regex_board = MISC::replace_str_list( m_list_abone_regex_board, "\\n", "\n" );
+    list_str = DBTREE::get_abone_list_regex_board( m_url );
+    list_str = MISC::replace_str_list( list_str, "\\n", "\n" );
+    m_list_abone_regex_board.clear();
+    for( std::list< std::string >::const_iterator it = list_str.begin();
+            it != list_str.end(); it++ ){
+        m_list_abone_regex_board.push_back( JDLIB::RegexPattern() );
+        m_list_abone_regex_board.rbegin()->set( *it, icase, newline, false, wchar );
+    }
 
     m_list_abone_word_global = MISC::replace_str_list( CONFIG::get_list_abone_word(), "\\n", "\n" );
-    m_list_abone_regex_global = MISC::replace_str_list( CONFIG::get_list_abone_regex(), "\\n", "\n" );
+    list_str = MISC::replace_str_list( CONFIG::get_list_abone_regex(), "\\n", "\n" );
+    m_list_abone_regex_global.clear();
+    for( std::list< std::string >::const_iterator it = list_str.begin();
+            it != list_str.end(); it++ ){
+        m_list_abone_regex_global.push_back( JDLIB::RegexPattern() );
+        m_list_abone_regex_global.rbegin()->set( *it, icase, newline, false, wchar );
+    }
 
     m_vec_abone_res = vec_abone_res;
 
@@ -2850,6 +3259,8 @@
     else m_abone_chain = abone_chain;
 
     m_abone_age = abone_age;
+    m_abone_default_name = abone_default_name;
+    m_abone_noid = abone_noid;
     m_abone_board = abone_board;
     m_abone_global = abone_global;
 }
@@ -2867,8 +3278,8 @@
     update_abone( 1, m_id_header );
 
     // 発言数更新
-    clear_id_name();
-    update_id_name( 1, m_id_header );
+    //clear_id_name();
+    //update_id_name( 1, m_id_header );
 
     // 参照状態更新
     clear_reference();
@@ -2928,23 +3339,32 @@
     const bool check_id = ! m_list_abone_id.empty();
     const bool check_id_board = ! m_list_abone_id_board.empty();
 
-    if( !check_id && !check_id_board ) return false;
+    if( !m_abone_noid && !check_id && !check_id_board ) return false;
 
     NODE* head = res_header( number );
     if( ! head ) return false;
     if( ! head->headinfo ) return false;
     if( head->headinfo->abone ) return true;
-    if( ! head->headinfo->block[ BLOCK_ID_NAME ] ) return false;
+    if( ! head->headinfo->block[ BLOCK_ID_NAME ] ){
+        // ID無し
+        if( m_abone_noid ){
+            head->headinfo->abone = true;
+            return true;
+        }
+        else return false;
+    }
+
+    NODE* idnode = head->headinfo->block[ BLOCK_ID_NAME ]->next_node;
+    if( ! idnode || ! idnode->linkinfo ) return false;
 
-    const int ln_protoid = strlen( PROTO_ID );
+    const char* protoid = idnode->linkinfo->link + strlen( PROTO_ID );
 
     // ローカルID
     if( check_id ){
         std::list< std::string >::iterator it = m_list_abone_id.begin();
         for( ; it != m_list_abone_id.end(); ++it ){
 
-            // std::string の find は遅いのでstrcmp使う
-            if( strcmp( head->headinfo->block[ BLOCK_ID_NAME ]->next_node->linkinfo->link + ln_protoid, ( *it ).c_str() ) == 0 ){
+            if( *protoid == ( *it )[ 0 ] && ( *it ).compare( protoid ) == 0 ){
                 head->headinfo->abone = true;
                 return true;
             }
@@ -2956,8 +3376,7 @@
         std::list< std::string >::iterator it = m_list_abone_id_board.begin();
         for( ; it != m_list_abone_id_board.end(); ++it ){
 
-            // std::string の find は遅いのでstrcmp使う
-            if( strcmp( head->headinfo->block[ BLOCK_ID_NAME ]->next_node->linkinfo->link + ln_protoid, ( *it ).c_str() ) == 0 ){
+            if( *protoid == ( *it )[ 0 ] && ( *it ).compare( protoid ) == 0 ){
                 head->headinfo->abone = true;
                 return true;
             }
@@ -2979,7 +3398,7 @@
     const bool check_name_board = ! m_list_abone_name_board.empty();
     const bool check_name_global = ! CONFIG::get_list_abone_name().empty();
 
-    if( !check_name && !check_name_board && !check_name_global ) return false;
+    if( !m_abone_default_name && !check_name && !check_name_board && !check_name_global ) return false;
 
     NODE* head = res_header( number );
     if( ! head ) return false;
@@ -2987,6 +3406,15 @@
     if( head->headinfo->abone ) return true;
     if( ! head->headinfo->name ) return false;
 
+    // デフォルト名無し
+    if( m_abone_default_name && head->headinfo->block[ BLOCK_NAMELINK ] ){
+        NODE* idnode = head->headinfo->block[ BLOCK_NAMELINK ]->next_node;
+        if( idnode && ! idnode->linkinfo ){
+            head->headinfo->abone = true;
+            return true;
+        }
+    }
+
     std::list< std::string >::const_iterator it;
     const std::string name_str( head->headinfo->name );
 
@@ -3078,15 +3506,11 @@
     const std::string res_str = get_res_str( number );
     JDLIB::Regex regex;
     const size_t offset = 0;
-    const bool icase = CONFIG::get_abone_icase();
-    const bool newline = true;
-    const bool usemigemo = false;
-    const bool wchar = CONFIG::get_abone_wchar();
 
     // ローカル NG word
     if( check_word ){
 
-        std::list< std::string >::iterator it = m_list_abone_word.begin();
+        std::list< std::string >::const_iterator it = m_list_abone_word.begin();
         for( ; it != m_list_abone_word.end(); ++it ){
             if( res_str.find( *it ) != std::string::npos ){
                 head->headinfo->abone = true;
@@ -3098,9 +3522,9 @@
     // ローカル NG regex
     if( check_regex ){
 
-        std::list< std::string >::iterator it = m_list_abone_regex.begin();
+        std::list< JDLIB::RegexPattern >::const_iterator it = m_list_abone_regex.begin();
         for( ; it != m_list_abone_regex.end(); ++it ){
-            if( regex.exec( *it, res_str, offset, icase, newline, usemigemo, wchar ) ){
+            if( regex.match( *it, res_str, offset ) ){
                 head->headinfo->abone = true;
                 return true;
             }
@@ -3110,7 +3534,7 @@
     // 板レベル NG word
     if( check_word_board ){
 
-        std::list< std::string >::iterator it = m_list_abone_word_board.begin();
+        std::list< std::string >::const_iterator it = m_list_abone_word_board.begin();
         for( ; it != m_list_abone_word_board.end(); ++it ){
             if( res_str.find( *it ) != std::string::npos ){
                 head->headinfo->abone = true;
@@ -3122,9 +3546,9 @@
     // 板レベル NG regex
     if( check_regex_board ){
 
-        std::list< std::string >::iterator it = m_list_abone_regex_board.begin();
+        std::list< JDLIB::RegexPattern >::const_iterator it = m_list_abone_regex_board.begin();
         for( ; it != m_list_abone_regex_board.end(); ++it ){
-            if( regex.exec( *it, res_str, offset, icase, newline, usemigemo, wchar ) ){
+            if( regex.match( *it, res_str, offset ) ){
                 head->headinfo->abone = true;
                 return true;
             }
@@ -3134,7 +3558,7 @@
     // 全体 NG word
     if( check_word_global ){
 
-        std::list< std::string >::iterator it = m_list_abone_word_global.begin();
+        std::list< std::string >::const_iterator it = m_list_abone_word_global.begin();
         for( ; it != m_list_abone_word_global.end(); ++it ){
             if( res_str.find( *it ) != std::string::npos ){
                 head->headinfo->abone = true;
@@ -3146,9 +3570,9 @@
     // 全体 NG regex
     if( check_regex_global ){
 
-        std::list< std::string >::iterator it = m_list_abone_regex_global.begin();
+        std::list< JDLIB::RegexPattern >::const_iterator it = m_list_abone_regex_global.begin();
         for( ; it != m_list_abone_regex_global.end(); ++it ){
-            if( regex.exec( *it, res_str, offset, icase, newline, usemigemo, wchar ) ){
+            if( regex.match( *it, res_str, offset ) ){
                 head->headinfo->abone = true;
                 return true;
             }
@@ -3257,8 +3681,8 @@
     if( head->headinfo->abone ) return;
 
     // 2重チェック防止用
-    bool checked[ MAX_RESNUMBER ];
-    memset( checked, 0, sizeof( bool ) * MAX_RESNUMBER );
+    unsigned char checked[ MAX_RESNUMBER / 8 + 1 ];
+    memset( checked, 0, sizeof( checked ) );
 
     const bool posted = m_vec_posted.size();
 
@@ -3329,7 +3753,7 @@
                         for( int i = anc_from; i <= anc_to ; ++i ){
 
                             // 既にチェックしている
-                            if( checked[ i ] ) continue;
+                            if( checked[ i / 8 ] & (1 << (i % 8)) ) continue;
 
                             // 自分自身
                             if( i == number ) continue;
@@ -3368,7 +3792,7 @@
                                 && tmphead->headinfo->block[ BLOCK_NUMBER ]
                                 ){
 
-                                checked[ i ] = true;
+                                checked[ i / 8 ] |= 1 << ( i % 8 );
 
                                 // 自分の書き込みに対するレス
                                 if( posted && m_vec_posted[ i ] ){
@@ -3454,24 +3878,39 @@
 //    if( header->headinfo->abone ) return;
     if( ! header->headinfo->block[ BLOCK_ID_NAME ] ) return;
 
-    const char* str_id = header->headinfo->block[ BLOCK_ID_NAME ]->next_node->linkinfo->link;
+    NODE* idnode = header->headinfo->block[ BLOCK_ID_NAME ]->next_node;
+    if( ! idnode || ! idnode->linkinfo ) return;
+
+    const char* str_id = idnode->linkinfo->link;
+
+    // ID:???はカウントしない
+    if( str_id[ sizeof( PROTO_ID ) + 2 ] == '?' ) return;
 
     // 同じIDのレスを持つ一つ前のレスを探す
-    NODE* tmphead;
-    NODE* prehead = NULL;
-    for( int i = header->id_header -1 ; i >= 1 ; --i ){
-
-        tmphead = m_vec_header[ i ];
-
-        if( tmphead
-//            && ! tmphead->headinfo->abone // 対象スレがあぼーんしていたらカウントしない
-            && tmphead->headinfo->block[ BLOCK_ID_NAME ]
-            && str_id[ 0 ] == tmphead->headinfo->block[ BLOCK_ID_NAME ]->next_node->linkinfo->link[ 0 ]
-            && strcmp( str_id, tmphead->headinfo->block[ BLOCK_ID_NAME ]->next_node->linkinfo->link ) == 0 ){
-            prehead = tmphead;
+    if( ! m_idhash ){
+        m_idhash = ( IDHASH** ) m_heap.heap_alloc( sizeof( IDHASH* ) * IDHASH_TBLSIZE );
+    }
+    size_t key = MISC::fnv_hash( str_id, strlen( str_id ) ) % IDHASH_TBLSIZE;
+    IDHASH* idhash = m_idhash[ key ];
+    if( ! idhash ){
+        idhash = ( IDHASH* )m_heap.heap_alloc( sizeof( IDHASH ) );
+        m_idhash[ key ] = idhash;
+        idhash->id = str_id;
+    }
+    else do{
+        const int cmp = strcmp( str_id, idhash->id );
+        if( cmp == 0 ) break;
+        if( ! idhash->child[ cmp>0 ] ){
+            idhash->child[ cmp>0 ] = ( IDHASH* )m_heap.heap_alloc( sizeof( IDHASH ) );
+            idhash = idhash->child[ cmp>0 ];
+            idhash->id = str_id;
             break;
         }
-    }
+        idhash = idhash->child[ cmp>0 ];
+    } while( true );
+
+    NODE* prehead = res_header( idhash->num );
+    idhash->num = number;
 
     // 見つからなかった
     if( ! prehead ) set_num_id_name( header, 1, NULL );
@@ -3482,7 +3921,7 @@
         set_num_id_name( header, prehead->headinfo->num_id_name+1, prehead );
 
         // 以前に出た同じIDのレスの発言数を更新
-        tmphead = prehead;
+        NODE* tmphead = prehead;
         while( tmphead ){
 
             set_num_id_name( tmphead, tmphead->headinfo->num_id_name+1, tmphead->headinfo->pre_id_name_header );
@@ -3499,14 +3938,16 @@
 //
 void NodeTreeBase::set_num_id_name( NODE* header, const int num_id_name, NODE* pre_id_name_header )
 {
-    if( ! header->headinfo->block[ BLOCK_ID_NAME ] ) return;
+    if( ! header->headinfo->block[ BLOCK_ID_NAME ] ||
+        ! header->headinfo->block[ BLOCK_ID_NAME ]->next_node ) return;
 
     header->headinfo->num_id_name = num_id_name;        
     header->headinfo->pre_id_name_header = pre_id_name_header;
 
-    if( num_id_name >= m_num_id[ LINK_HIGH ] ) header->headinfo->block[ BLOCK_ID_NAME ]->next_node->color_text = COLOR_CHAR_LINK_ID_HIGH;
-    else if( num_id_name >= m_num_id[ LINK_LOW ] ) header->headinfo->block[ BLOCK_ID_NAME ]->next_node->color_text = COLOR_CHAR_LINK_ID_LOW;
-    else header->headinfo->block[ BLOCK_ID_NAME ]->next_node->color_text = COLOR_CHAR;
+    NODE* idnode = header->headinfo->block[ BLOCK_ID_NAME ]->next_node;
+    if( num_id_name >= m_num_id[ LINK_HIGH ] ) idnode->color_text = COLOR_CHAR_LINK_ID_HIGH;
+    else if( num_id_name >= m_num_id[ LINK_LOW ] ) idnode->color_text = COLOR_CHAR_LINK_ID_LOW;
+    else idnode->color_text = COLOR_CHAR;
 }
 
 
@@ -3533,7 +3974,7 @@
 
     // ヘッダノードには、フォント判定済みの意味を兼ねて、デフォルトフォントを設定しておく
     head->fontid = FONT_DEFAULT;
-    if( m_aa_regex.empty() ) return;
+    if( ! m_aa_regex.compiled() ) return;
 
     char fontid_mes = FONT_DEFAULT; // 本文のフォント(fontid.h)
 
@@ -3541,12 +3982,8 @@
     const std::string res_str = get_res_str( number );
     JDLIB::Regex regex;
     const size_t offset = 0;
-    const bool icase = false;
-    const bool newline = true;
-    const bool usemigemo = false;
-    const bool wchar = false;
 
-    if( regex.exec( m_aa_regex, res_str, offset, icase, newline, usemigemo, wchar ) ){
+    if( regex.match( m_aa_regex, res_str, offset ) ){
         fontid_mes = FONT_AA;
 #ifdef _DEBUG
         std::cout << "NodeTreeBase::check_fontid() fontid = " << FONT_AA
@@ -3578,64 +4015,52 @@
 //
 bool NodeTreeBase::remove_imenu( char* str_link )
 {
-    const int lng_http = 7; // = strlen( "http://" );
+    int lng_cut = 4;
+    char *str_colon = str_link + lng_cut;
+    if( memcmp( str_link, "http", lng_cut ) == 0 ){
+        if( str_link[ lng_cut ] == 's' ){
+            lng_cut++;
+            str_colon++;
+        }
+    }
+    else return false;
 
     if(
       ! (
-        ( str_link[ lng_http ] == 'i' && str_link[ lng_http + 1 ] == 'm' )
-        || ( str_link[ lng_http ] == 'n' && str_link[ lng_http + 1 ] == 'u' )
-        || ( str_link[ lng_http ] == 'p' && str_link[ lng_http + 1 ] == 'i' )
+        ( str_colon[ 3 ] == 'i' && str_colon[ 4 ] == 'm' )
+        || ( str_colon[ 3 ] == 'n' && str_colon[ 4 ] == 'u' )
+        || ( str_colon[ 3 ] == 'p' && str_colon[ 4 ] == 'i' )
+        || ( str_colon[ 3 ] == 'j' && str_colon[ 4 ] == 'u' )
       )
     ) return false;
 
-    if( memcmp( str_link, "http://ime.nu/", 14 ) == 0
-        || memcmp( str_link, "http://ime.st/", 14 ) == 0
-        || memcmp( str_link, "http://nun.nu/", 14 ) == 0
-        || memcmp( str_link, "http://pinktower.com/", 21 ) == 0 )
-    {
-		int linklen = strlen( str_link );
-		int cutsize = 0; 
-
-		if( str_link[ lng_http ] == 'p' ) cutsize = 14; // = strlen( "pinktower.com/" )
-		else cutsize =  7; // = strlen( "ime.nu/" )
-
-        // "http://ime.nu/"等、URLがそれだけだった場合は削除しない
-        if( linklen == lng_http + cutsize ) return false;
-
-		memmove( str_link + lng_http, str_link + lng_http + cutsize, linklen + 1 - ( lng_http + cutsize ) );
-
-		return true;
-	}
-
-    return false;
-}
-
-
-// 文字列中の"&amp;"を"&"に変換する
-int NodeTreeBase::convert_amp( char* text, const int n )
-{
-    int m = n;
-
-    int i;
-    for( i = 0; i < m; i++ ){
-
-        if( text[ i ] == '&' &&
-            m > (i + 4) &&
-            text[i + 1] == 'a' &&
-            text[i + 2] == 'm' &&
-            text[i + 3] == 'p' &&
-            text[i + 4] == ';' ){
-
-            // &の次, &amp;の次, &amp;の次からの長さ
-            memmove( text + i + 1, text + i + 5, n - i - 5 );
-
-            // "amp;"の分減らす
-            m -= 4;
-        }
+    if( memcmp( str_colon, "://ime.nu/", 10 ) == 0 ||
+        memcmp( str_colon, "://ime.st/", 10 ) == 0 ||
+        memcmp( str_colon, "://nun.nu/", 10 ) == 0 ){
+        lng_cut += 10;
+    }
+    else if( memcmp( str_colon, "://pinktower.com/", 17 ) == 0 ||
+             memcmp( str_colon, "://jump.2ch.net/?", 17 ) == 0 ||
+             memcmp( str_colon, "://jump.5ch.net/?", 17 ) == 0 ){
+        lng_cut += 17;
+    }
+    else return false;
+
+    int lng_link = strlen( str_link );
+
+    // "http://ime.nu/"等、URLがそれだけだった場合は削除しない
+    if( lng_link == lng_cut ) return false;
+
+    if( memcmp( str_link + lng_cut, "http", 4 ) == 0 ){
+        // プロトコルが続く場合は頭から削る
+        memmove( str_link, str_link + lng_cut, lng_link + 1 - lng_cut );
+    }
+    else{
+        // 上記以外はプロトコルを残して削る
+        memmove( str_colon + 3, str_link + lng_cut, lng_link + 1 - lng_cut );
     }
 
-    text[m] = '\0';
-    return m;
+    return true;
 }
 
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetreebase.h jd-2.8.9-a171004/src/dbtree/nodetreebase.h
--- jd-2.8.9-150226/src/dbtree/nodetreebase.h	2015-01-25 15:54:29.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetreebase.h	2017-04-01 22:39:05.678565000 +0900
@@ -13,6 +13,7 @@
 
 #include "jdlib/heap.h"
 #include "jdlib/miscutil.h"
+#include "jdlib/jdregex.h"
 
 #include <map>
 #include <cstring>
@@ -36,6 +37,8 @@
         RESUME_CHKSIZE = 64
     };
 
+    struct IDHASH { const char *id; int num; IDHASH* child[2]; };
+
     //ノードツリーのベースクラス
     class NodeTreeBase : public SKELETON::Loadable
     {
@@ -45,6 +48,7 @@
         SIG_UPDATED m_sig_finished;
 
         std::string m_url;
+        std::string m_url_readcgi;
         std::string m_default_noname;
 
         // コード変換前の生データのサイズ ( byte )
@@ -83,19 +87,21 @@
         std::list< std::string > m_list_abone_id;   // あぼーんするID
         std::list< std::string > m_list_abone_name; // あぼーんする名前
         std::list< std::string > m_list_abone_word; // あぼーんする文字列
-        std::list< std::string > m_list_abone_regex; // あぼーんする正規表現
+        std::list< JDLIB::RegexPattern > m_list_abone_regex; // あぼーんする正規表現
 
         std::list< std::string > m_list_abone_id_board;   // あぼーんするID(板レベル)
         std::list< std::string > m_list_abone_name_board; // あぼーんする名前(板レベル)
         std::list< std::string > m_list_abone_word_board; // あぼーんする文字列(板レベル)
-        std::list< std::string > m_list_abone_regex_board; // あぼーんする正規表現(板レベル)
+        std::list< JDLIB::RegexPattern > m_list_abone_regex_board; // あぼーんする正規表現(板レベル)
 
         std::list< std::string > m_list_abone_word_global; // あぼーんする文字列(全体)
-        std::list< std::string > m_list_abone_regex_global; // あぼーんする正規表現(全体)
+        std::list< JDLIB::RegexPattern > m_list_abone_regex_global; // あぼーんする正規表現(全体)
         std::vector< char > m_vec_abone_res; // レスあぼーん情報
         bool m_abone_transparent; // 透明あぼーん
         bool m_abone_chain; // 連鎖あぼーん
         bool m_abone_age; // age ているレスはあぼーん
+        bool m_abone_default_name; // デフォルト名無しのレスはあぼーん
+        bool m_abone_noid; // ID無しのレスはあぼーん
         bool m_abone_board; // 板レベルでのあぼーんを有効にする
         bool m_abone_global; // 全体レベルでのあぼーんを有効にする
 
@@ -125,11 +131,14 @@
         NODE* m_node_previous;
 
         // AA判定用
-        std::string m_aa_regex;
+        JDLIB::RegexPattern m_aa_regex;
 
         // その他のエラーメッセージ
         std::string m_ext_err;
 
+        // IDのハッシュテーブル
+        IDHASH **m_idhash;
+
       protected:
 
         void set_resume( const bool resume );
@@ -159,6 +168,8 @@
         const std::string& get_ext_err() const { return m_ext_err; }
         const bool is_checking_update() const { return m_check_update; }
 
+        virtual int get_res_number_max(){ return -1; }
+
         // number番のレスのヘッダノードのポインタを返す
         NODE* res_header( int number );
 
@@ -197,7 +208,7 @@
         std::list< int > get_res_with_url();
 
         // 含まれる URL をリストにして取得
-        std::list< std::string > get_urls();
+        const std::list< std::string > get_imglinks();
 
         // number番のレスを参照しているレス番号をリストにして取得
         std::list< int > get_res_reference( const int number );
@@ -214,9 +225,6 @@
         // ref == true なら先頭に ">" を付ける        
         const std::string get_res_str( int number, bool ref = false );
 
-        // number 番のレスの生文字列を返す
-        const std::string get_raw_res_str( int number );
-        
         // 明示的にhtml を加える
         // パースして追加したノードのポインタを返す
         // html は UTF-8 であること
@@ -241,6 +249,7 @@
                               const std::list< std::string >& list_abone_regex,
                               const std::vector< char >& vec_abone_res,
                               const bool abone_transparent, const bool abone_chain, const bool abone_age,
+                              const bool abone_default_name, const bool abone_noid,
                               const bool abone_board, const bool abone_global );
 
         // 全レスのあぼーん状態の更新
@@ -270,7 +279,7 @@
 
         // 保存前にrawデータを加工
         // デフォルトでは何もしない
-        virtual char* process_raw_lines( char* rawlines ){ return rawlines; }
+        virtual char* process_raw_lines( char* rawlines, size_t& size ){ return rawlines; }
 
         // raw データを dat に変換
         // デフォルトでは何もしない
@@ -281,6 +290,10 @@
 
         virtual void receive_data( const char* data, size_t size );
         virtual void receive_finish();
+        void sweep_buffer();
+
+        // 格調属性を取り出す
+        virtual void parse_extattr( const char* str, const int lng ){};
 
       private:
 
@@ -290,21 +303,20 @@
         NODE* create_node_idnum();
         NODE* create_node_br();
         NODE* create_node_hr();
-        NODE* create_node_space( const int type );
-        NODE* create_node_multispace( const char* text, const int n );
-        NODE* create_node_htab();
-        NODE* create_node_link( const char* text, const int n, const char* link, const int n_link, const int color_text, const bool bold );
+        NODE* create_node_space( const int type, const int bg );
+        NODE* create_node_multispace( const char* text, const int n, const int bg );
+        NODE* create_node_link( const char* text, const int n, const char* link, const int n_link, const int color_text, const int color_back, const bool bold );
         NODE* create_node_anc( const char* text, const int n, const char* link, const int n_link,
                                const int color_text, const bool bold,
                                const ANCINFO* ancinfo, const int lng_ancinfo );
         NODE* create_node_sssp( const char* link, const int n_link );
         NODE* create_node_img( const char* text, const int n, const char* link, const int n_link, const int color_text, const bool bold );
         NODE* create_node_text( const char* text, const int color_text, const bool bold = false );
-        NODE* create_node_ntext( const char* text, const int n, const int color_text, const bool bold = false );
+        NODE* create_node_ntext( const char* text, const int n, const int color_text, const int color_back = 0, const bool bold = false );
         NODE* create_node_thumbnail( const char* text, const int n, const char* link, const int n_link, const char* thumb, const int n_thumb, const int color_text, const bool bold );
 
         // 以下、構文解析用関数
-        void add_raw_lines( char* rawines, size_t size );
+        size_t add_raw_lines( char* rawines, size_t size );
         const char* add_one_dat_line( const char* datline );
 
         void parse_name( NODE* header, const char* str, const int lng, const int color_name );
@@ -315,17 +327,14 @@
         // digitlink : true の時は先頭に数字が現れたらアンカーにする( parse_name() などで使う )
         //             false なら数字の前に >> がついてるときだけアンカーにする
         // bold : ボールド表示
-        // ahref : <a href=~></a> からリンクノードを作成する
-        void parse_html( const char* str, const int lng, const int color_text,
-                         bool digitlink, const bool bold, const bool ahref );
+        void parse_html( const char* str, const int lng, const int color_text, bool digitlink, const bool bold );
 
         // 書き込みログ比較用文字列作成
         // m_buffer_write に作成した文字列をセットする
         void parse_write( const char* str, const int lng, const int max_lng_write );
 
         const bool check_anchor( const int mode, const char* str_in, int& n, char* str_out, char* str_link, int lng_link, ANCINFO* ancinfo );
-        const int check_link( const char* str_in, const int lng_in, int& n_in, char* str_link, const int lng_link );
-        const int check_link_impl( const char* str_in, const int lng_in, int& n_in, char* str_link, const int lng_link, const int linktype, const int delim_pos );
+        const int check_link( const char* str_in, int& lng_in, char* str_text, size_t& lng_text, char* str_link, size_t& lng_link );
 
         // レジューム時のチェックデータをキャッシュ
         void set_resume_data( const char* data, size_t length );
@@ -384,26 +393,7 @@
 
         // http://ime.nu/ などをリンクから削除
         bool remove_imenu( char* str_link );
-
-        // 文字列中の"&amp;"を"&"に変換する
-        int convert_amp( char* text, const int n );
     };
-
-
-    //
-    // リンクが現れたかチェックして文字列を取得する関数
-    //   (引数の値は、check_link_impl()を見ること)
-    //
-    inline const int NodeTreeBase::check_link( const char* str_in, const int lng_in, int& n_in, char* str_link, const int lng_link )
-    {
-        // http://, https://, ftp://, ttp(s)://, tp(s):// のチェック
-        int delim_pos = 0;
-        const int linktype = MISC::is_url_scheme( str_in, &delim_pos );
-
-        if( linktype == MISC::SCHEME_NONE ) return linktype;
-
-        return check_link_impl( str_in, lng_in, n_in, str_link, lng_link, linktype, delim_pos );
-    }
 }
 
 #endif
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetreejbbs.cpp jd-2.8.9-a171004/src/dbtree/nodetreejbbs.cpp
--- jd-2.8.9-150226/src/dbtree/nodetreejbbs.cpp	2014-04-28 23:43:24.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetreejbbs.cpp	2016-02-14 23:32:33.142392000 +0900
@@ -7,13 +7,15 @@
 #include "interface.h"
 
 #include "jdlib/jdiconv.h"
+#include "jdlib/miscutil.h"
 #include "jdlib/loaderdata.h"
 
 #include "config/globalconf.h"
 
-#include "global.h"
+#include "httpcode.h"
+#include "session.h"
 
-#include <sstream>
+#include <strings.h>
 
 
 #define APPEND_SECTION( num ) do {\
@@ -27,10 +29,18 @@
 using namespace DBTREE;
 
 
+enum
+{
+    MODE_NORMAL = 0,
+    MODE_KAKO
+};
+
+
 NodeTreeJBBS::NodeTreeJBBS( const std::string& url, const std::string& date_modified )
     : NodeTreeBase( url, date_modified )
     , m_iconv( NULL )
     , m_decoded_lines( NULL )
+    , m_mode( MODE_NORMAL )
 {
 #ifdef _DEBUG
     std::cout << "NodeTreeJBBS::NodeTreeJBBS url = " << get_url() << " modified = " << date_modified << std::endl;
@@ -44,7 +54,12 @@
     std::cout << "NodeTreeJBBS::~NodeTreeJBBS : " << get_url() << std::endl;
 #endif
 
-    clear();
+    // iconv 削除
+    if( m_iconv ) delete m_iconv;
+    m_iconv = NULL;
+
+    if( m_decoded_lines ) free( m_decoded_lines );
+    m_decoded_lines = NULL;
 }
 
 
@@ -80,8 +95,7 @@
     NodeTreeBase::init_loading();
 
     // iconv 初期化
-    std::string charset = DBTREE::board_charset( get_url() );
-    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( charset, "UTF-8" );
+    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( DBTREE::article_charcode( get_url() ), CHARCODE_UTF8 );
 
     if( ! m_decoded_lines ) m_decoded_lines = ( char* )malloc( BUF_SIZE_ICONV_OUT );
 }
@@ -94,14 +108,13 @@
 //
 void NodeTreeJBBS::create_loaderdata( JDLIB::LOADERDATA& data )
 {
-    std::stringstream ss;
-    ss << get_url() << "/";
+    if( m_mode == MODE_KAKO ) data.url = MISC::replace_str( get_url(), "rawmode", "read_archive" ) + "/";
+    else data.url = get_url() + "/";
+    if( id_header() ) data.url += MISC::itostr( id_header() + 1 ) + "-";
 
-    // レジュームはしない代わりにスレを直接指定
+    // レジュームはしない
     set_resume( false );
-    if( id_header() ) ss << id_header() + 1 << "-";
 
-    data.url = ss.str();
     data.agent = DBTREE::get_agent( get_url() );
     data.host_proxy = DBTREE::get_proxy_host( get_url() );
     data.port_proxy = DBTREE::get_proxy_port( get_url() );
@@ -118,6 +131,52 @@
 
 
 //
+// キャッシュに保存する前の前処理
+//
+char* NodeTreeJBBS::process_raw_lines( char* rawlines, size_t& size )
+{
+    if( m_mode == MODE_KAKO ) html2dat( rawlines );
+
+    return rawlines;
+}
+
+
+//
+// ロード完了
+//
+void NodeTreeJBBS::receive_finish()
+{
+#ifdef _DEBUG
+    std::cout << "NodeTreeJBBS::receive_finish : " << get_url() << std::endl
+              << "mode = " << m_mode << " code = " << get_code() << std::endl;
+#endif
+
+    // 更新チェックではない、オンラインの場合はログ倉庫から取得出来るか試みる
+    if( ! is_checking_update() && SESSION::is_online()
+            && m_mode == MODE_NORMAL && get_error() == "STORAGE IN" ){
+
+        m_mode = MODE_KAKO;
+#ifdef _DEBUG
+        std::cout << "switch mode to " << m_mode << std::endl;
+#endif
+        set_date_modified( std::string() );
+        download_dat( false );
+        return;
+    }
+
+    if( ! get_error().empty() ) set_str_code( get_error() );
+
+    // 過去ログから読み込んだ場合は DAT 落ちにする
+    if( m_mode != MODE_NORMAL ){
+        set_code( HTTP_OLD );
+    }
+
+    NodeTreeBase::receive_finish();
+    m_mode = MODE_NORMAL;
+}
+
+
+//
 // raw データを dat 形式に変換
 //
 
@@ -260,3 +319,165 @@
 
     return m_decoded_lines;
 }
+
+
+void NodeTreeJBBS::html2dat( char* lines )
+{
+    char* ps( lines );
+    char* pd( lines );
+    char* nextline( lines );
+    char* pos;
+    std::string title;
+    int num_next = id_header() + 1;
+
+    while( (pos = strchr( nextline, '\n' ) ) != NULL ){
+        *pos = '\0';
+        ps = nextline;
+        nextline = pos + 1;
+        if( ( pos = strstr( ps, "<dt><a name=\"" ) ) == NULL ){
+            if( ( pos = strstr( ps, "<title>" ) ) != NULL ){
+                // title
+                pos += 7;
+                char* epos;
+                if( ( epos = strstr( pos, "</title>" ) ) != NULL )
+                    title.assign( pos, epos - pos );
+            }
+        }
+        else{
+            int num;
+            pos += 13;
+
+            // number
+            while( *pos != '>' && *pos != '\0' ) ++pos;
+            if( *pos == '\0' ) continue;
+            ps = pos + 1;
+            pos = strstr( ps, "</a>" );
+            if( pos == NULL || *pos == '\0' ) continue;
+            *pos = '\0';
+            num = atoi( ps );
+
+            // 既に読み込んでいる場合は飛ばす
+            if( num < num_next ) continue;
+
+            while( ps != pos ) *(pd++) = *(ps++);
+            if( *( pd - 1 ) == ' ' ) --pd;
+            *(pd++) = '<'; *(pd++) = '>';
+            pos += 4;
+            while( *pos != '<' && *pos != '\0' ) ++pos;
+            if( pos == NULL || *pos == '\0' ) continue;
+            ps = pos;
+
+            // mail
+            std::string mail;
+            if( memcmp( ps, "<a href=\"mailto:", 16 ) == 0 ){
+                pos = ps + 16;
+                while( *pos != '\"' && *pos != '\0' ) mail.push_back( *pos++ );
+                if( *pos == '\0' ) continue;
+                while( *pos != '>' && *pos != '\0' ) ++pos;
+                if( *pos == '\0' ) continue;
+                else ++pos;
+                ps = pos;
+            }
+
+            char* body;
+            if( ( body = strstr( ps, "<dd>" ) ) == NULL ) continue;
+            else *body = '\0';
+
+            // name
+            if( memcmp( ps, "<font color=\"#008800\">", 22 ) == 0 ) ps += 22;
+            if( memcmp( ps, "<b>", 3 ) == 0 ) ps += 3;
+            pos = body;
+            while( pos != ps && !( *pos == '\241' && *( pos + 1 ) == '\247' ) ) --pos;
+            if( pos == ps ) continue;
+            char* date = pos + 2;
+            while( pos != ps && !( *pos == ' ' && *( pos + 1 ) == '\305'
+                                    && *( pos + 2 ) == '\352' ) ) --pos;
+            if( pos == ps ) pos = date - 2;
+            if( *ps == ' ' ) ++ps;
+            memmove( pd, ps, pos - ps ); pd += pos - ps;
+            if( strncasecmp( pd - 7, "</font>", 7 ) == 0 ) pd -= 7;
+            if( strncasecmp( pd - 4, "</a>", 4 ) == 0 ) pd -= 4;
+            if( strncasecmp( pd - 4, "</b>", 4 ) == 0 ) pd -= 4;
+            if( *( pd - 1 ) == ' ' ) --pd;
+            *(pd++) = '<'; *(pd++) = '>';
+            mail.copy( pd, mail.length() );
+            pd += mail.length();
+            *(pd++) = '<'; *(pd++) = '>';
+            ps = date;
+            if( *ps == ' ' ) ++ps;
+
+            // date
+            pos = strstr( ps, " ID:" );
+            if( pos == NULL ) pos = strstr( ps, " <!--" );
+            if( pos == NULL || *pos == '\0' ) pos = body;
+            memmove( pd, ps, pos - ps );
+            pd += pos - ps;
+            if( *( pd - 1 ) == ' ' ) --pd;
+            if( strncasecmp( pd - 4, "<br>", 4 ) == 0 ) pd -= 4;
+            *(pd++) = '<'; *(pd++) = '>';
+
+            // id
+            std::string id;
+            if( pos != body ){
+                pos += 4;
+                if( *pos == '-' ) pos += 4;
+                ps = pos;
+                while( *pos != ' ' && *pos != '\0' ) ++pos;
+                *pos = '\0';
+                id = ps;
+                if( *id.end() == ' ' ) id.resize( id.size() - 1 );
+            }
+            ps = body + 4;
+            if( *ps == ' ' ) ++ps;
+
+            // message
+            while( ( pos = strchr( ps, '<' ) ) != NULL ){
+
+                if( pos != ps ){
+                    memmove( pd, ps, pos - ps );
+                    pd += pos - ps;
+                    ps = pos;
+                }
+
+                else if( ( memcmp( ps, "<a href=\"", 9 ) == 0 )
+                        && ( *( ps + 9 ) != '.' && *( ps + 9 ) != '/' ) ){
+                    // 外部リンクのURL
+                    if( ( pos = strchr( ps + 9, '>' ) ) != NULL ){
+                        ps = pos + 1;
+                        if( ( pos = strstr( ps, "</a>" ) ) != NULL ){
+                            memmove( pd, ps, pos - ps );
+                            pd += pos - ps;
+                            ps = pos + 4;
+                        }
+                    }
+                }
+
+                else {
+                    *(pd++) = '<';
+                    ps += 1;
+                }
+            }
+
+            pos = ps;
+            while( *pos != '\0' ) *(pd++) = *(pos++);
+            if( memcmp( pd - 8, "<br><br>", 8 ) == 0 ) pd -= 8;
+            if( *( pd - 1 ) == ' ' ) --pd;
+            *(pd++) = '<'; *(pd++) = '>';
+
+            if( num == 1 ){
+                title.copy( pd, title.length() );
+                pd += title.length();
+            }
+            *(pd++) = '<'; *(pd++) = '>';
+
+            if( ! id.empty() ){
+                id.copy( pd, id.length() );
+                pd += id.length();
+            }
+
+            *(pd++) = '\n';
+            num_next = num + 1;
+        }
+    }
+    *pd = '\0';
+}
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetreejbbs.h jd-2.8.9-a171004/src/dbtree/nodetreejbbs.h
--- jd-2.8.9-150226/src/dbtree/nodetreejbbs.h	2009-01-29 23:43:42.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetreejbbs.h	2015-11-14 05:26:59.462455000 +0900
@@ -21,6 +21,7 @@
     {
         JDLIB::Iconv* m_iconv;
         char* m_decoded_lines;
+        int m_mode; // 読み込みモード
         
       public:
 
@@ -32,7 +33,13 @@
         virtual void clear();
         virtual void init_loading();
         virtual void create_loaderdata( JDLIB::LOADERDATA& data );
+        virtual char* process_raw_lines( char* rawlines, size_t& size );
         virtual const char* raw2dat( char* rawlines, int& byte );
+
+      private:
+
+        virtual void receive_finish();
+        void html2dat( char* lines );
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetreemachi.cpp jd-2.8.9-a171004/src/dbtree/nodetreemachi.cpp
--- jd-2.8.9-150226/src/dbtree/nodetreemachi.cpp	2014-04-28 23:43:24.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetreemachi.cpp	2016-09-06 17:09:10.937262000 +0900
@@ -9,6 +9,7 @@
 #include "jdlib/jdiconv.h"
 #include "jdlib/jdregex.h"
 #include "jdlib/loaderdata.h"
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/miscmsg.h"
 
@@ -98,8 +99,7 @@
     if( ! m_regex ) m_regex = new JDLIB::Regex();
 
     // iconv 初期化
-    std::string charset = DBTREE::board_charset( get_url() );
-    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( charset, "UTF-8" );
+    if( ! m_iconv ) m_iconv = new JDLIB::Iconv( DBTREE::article_charcode( get_url() ), CHARCODE_UTF8 );
 
     if( ! m_decoded_lines ) m_decoded_lines = ( char* )malloc( BUF_SIZE_ICONV_OUT );
     if( ! m_buffer ) m_buffer = ( char* )malloc( BUF_SIZE_ICONV_OUT + 64 );
@@ -163,7 +163,7 @@
 //
 // キャッシュに保存する前の前処理
 //
-char* NodeTreeMachi::process_raw_lines( char* rawlines )
+char* NodeTreeMachi::process_raw_lines( char* rawlines, size_t& size )
 {
     // オフラインか offlaw 形式を使用する場合はそのまま返す
     if( ! is_loading() || CONFIG::get_use_machi_offlaw() ) return rawlines;
@@ -208,10 +208,9 @@
                 std::string reg_subject( "<title>([^<]*)</title>" );
                 if( m_regex->exec( reg_subject, line, offset, icase, newline, usemigemo, wchar ) ){
 
-                    const std::string charset = DBTREE::board_charset( get_url() );
-                    m_subject_machi = MISC::Iconv( m_regex->str( 1 ), charset, "UTF-8" );
+                    m_subject_machi = MISC::Iconv( m_regex->str( 1 ), get_charcode(), CHARCODE_UTF8 );
 #ifdef _DEBUG
-                    std::cout << "NodeTreeMachi::process_raw_lines\n";
+                    std::cout << "NodeTreeMachi::process_raw_lines" << std::endl;
                     std::cout << "subject = " << m_subject_machi << std::endl;
 #endif
                 }
@@ -304,7 +303,7 @@
         // read.cgi 形式
         else{
 
-            std::string reg( "<dt>([1-9][0-9]*) ?名前:(<a href=\"mailto:([^\"]*)\"><b>|<font[^>]*><b>) ?(<font[^>]*>)?([^<]*)(</font>)? ?</[bB]>.+ ?投稿日: ?([^<]*)( <font[^>]*>\\[ ?(.*) ?\\]</font>)?<br><dd> ?(.*) ?<br><br>$" );
+            std::string reg( "<dt>([1-9][0-9]*) ?名前:(<a href=\"mailto:([^\"]*)\"><b>|<font[^>]*><b>) ?(<font[^>]*>)?([^<]*)(</font>)? ?</[bB]>.+ ?投稿日: ?([^<]*)( <font[^>]*>\\[ ?(.*) ?\\]</font>)?<br><dd> ?(.*) ?<br><br>(<script [^>]+>)?$" );
 
             if( ! m_regex->exec( reg, line, offset, icase, newline, usemigemo, wchar ) ){
 #ifdef _DEBUG
diff -aurNZ jd-2.8.9-150226/src/dbtree/nodetreemachi.h jd-2.8.9-a171004/src/dbtree/nodetreemachi.h
--- jd-2.8.9-150226/src/dbtree/nodetreemachi.h	2010-02-21 00:33:54.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/nodetreemachi.h	2015-11-14 05:26:59.478455000 +0900
@@ -40,7 +40,7 @@
         virtual void clear();
         virtual void init_loading();
         virtual void create_loaderdata( JDLIB::LOADERDATA& data );
-        virtual char* process_raw_lines( char* rawlines );
+        virtual char* process_raw_lines( char* rawlines, size_t& size );
         virtual const char* raw2dat( char* rawlines, int& byte );
 
         virtual void receive_data( const char* data, size_t size );
diff -aurNZ jd-2.8.9-150226/src/dbtree/root.cpp jd-2.8.9-a171004/src/dbtree/root.cpp
--- jd-2.8.9-150226/src/dbtree/root.cpp	2014-04-13 15:48:03.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/root.cpp	2017-10-02 02:17:42.917515329 +0900
@@ -139,6 +139,8 @@
 
     const int max_count = 50;
 
+    if( !CONFIG::get_url_api2ch().compare( 0, CONFIG::get_url_api2ch().length(), url, 0, CONFIG::get_url_api2ch().length() ) ) return m_board_null;
+
     // キャッシュ
     if( m_get_board ){
 
@@ -168,13 +170,14 @@
     if( count == 0 ){
 
         size_t pos = url.rfind( "http://" );
+        size_t pos2 = url.rfind( "https://" );
 
         // ユーザープロフィールアドレス( http://be.2ch.net/test/p.php?u=d:http://〜 )の様に
         // 先頭以外に http:// が入っている場合は失敗
-        if( pos != std::string::npos && pos != 0 ) return m_board_null;
+        if( ( pos != std::string::npos && pos != 0 ) || ( pos2 != std::string::npos && pos2 != 0 ) ) return m_board_null;
 
         // http:// が含まれていなかったら先頭に追加して再帰呼び出し
-        else if( pos == std::string::npos && ! is_local( url ) ){
+        else if( pos == std::string::npos && pos2 == std::string::npos && ! is_local( url ) ){
             BoardBase* board = get_board( "http://" + url , count + 1 );
             m_get_board_url = url;
             return board;
@@ -273,12 +276,7 @@
 {
     clear();
 
-    // ファイルが存在しなければ入力を旧ファイル名にする
     std::string file_in = CACHE::path_xml_listmain();
-    if( CACHE::file_exists( file_in ) != CACHE::EXIST_FILE )
-    {
-        file_in = CACHE::path_xml_listmain_old();
-    }
 
 #ifdef _DEBUG
     std::cout << "Root::load_cache xml  = " << file_in << std::endl;
@@ -365,7 +363,7 @@
     }
 
     // 文字コードを変換してXML作成
-    JDLIB::Iconv* libiconv = new JDLIB::Iconv( "MS932", "UTF-8" );
+    JDLIB::Iconv* libiconv = new JDLIB::Iconv( CHARCODE_SJIS, CHARCODE_UTF8 );
     int byte_out;
     const char* rawdata_utf8 = libiconv->convert( m_rawdata , m_lng_rawdata,  byte_out );
     bbsmenu2xml( rawdata_utf8 );
@@ -418,13 +416,14 @@
 
     // 現在の仕様では HTML > BODY > font[size="2"] の子要素が対象
     XML::DomList targets = html.getElementsByTagName( "font" )[0]->childNodes();
+    if( targets.empty() ) targets = html.getElementsByTagName( "small" )[0]->childNodes();
     std::list< XML::Dom* >::iterator it = targets.begin();
     while( it != targets.end() )
     {
         // 要素b( カテゴリ名 )
         if( (*it)->nodeName() == "b" )
         {
-            const std::string category = (*it)->firstChild()->nodeValue();
+            const std::string category = MISC::chref_decode( (*it)->firstChild()->nodeValue() );
 
             // 追加しないカテゴリ
             if( category == "チャット"
@@ -444,13 +443,13 @@
         // 要素bに続く要素a( 板URL )
         else if( subdir && enabled && (*it)->nodeName() == "a" )
         {
-            const std::string board_name = (*it)->firstChild()->nodeValue();
+            const std::string board_name = MISC::chref_decode( (*it)->firstChild()->nodeValue() );
             const std::string url = (*it)->getAttribute( "href" );
 
             // 板として扱うURLかどうかで要素名を変える
             std::string element_name;
             if( CONFIG::use_link_as_board() ) element_name = "board";
-            else if( ( regex.exec( "^http://.*/.*/$", url, offset, icase, newline, usemigemo, wchar )
+            else if( ( regex.exec( "^(https?:)?//.*/.*/$", url, offset, icase, newline, usemigemo, wchar )
 			            && ( is_2ch( url ) || is_machi( url ) ) )
                      || is_JBBS( url )
                      || is_vip2ch( url )
@@ -541,11 +540,11 @@
     int type = TYPE_BOARD_UNKNOWN;
 
     // 2ch
-    if( ! etc && is_2ch( url ) ){
+    if( /*! etc &&*/ is_2ch( url ) ){
 
-        if( regex.exec( "(http://[^/]*)/([^/]*)/$" , url, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.exec( "(https?://[^/]*)(/[^/]*)/$" , url, offset, icase, newline, usemigemo, wchar ) ){
             root = regex.str( 1 );
-            path_board = "/" + regex.str( 2 );
+            path_board = regex.str( 2 );
 
             type = TYPE_BOARD_2CH;
         }
@@ -554,9 +553,9 @@
     // JBBS
     else if( is_JBBS( url ) ){
 
-        if( regex.exec( "(http://[^/]*)/(.*)/(index2?\\.html?)?$" , url, offset, icase, newline, usemigemo, wchar ) ){
-            root = "http://jbbs.shitaraba.net";
-            path_board = "/" + regex.str( 2 );
+        if( regex.exec( "(https?://)[^/]*(/.*)/(index2?\\.html?)?$" , url, offset, icase, newline, usemigemo, wchar ) ){
+            root = regex.str( 1 ) + "jbbs.shitaraba.net";
+            path_board = regex.str( 2 );
 
             type = TYPE_BOARD_JBBS;
         }
@@ -565,9 +564,9 @@
     // まち
     else if( is_machi( url ) ){
 
-        if( regex.exec( "(http://[^/]*)/([^/]*)/(index2?\\.html?)?$" , url, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.exec( "(https?://[^/]*)(/[^/]*)/(index2?\\.html?)?$" , url, offset, icase, newline, usemigemo, wchar ) ){
             root = regex.str( 1 );
-            path_board = "/" + regex.str( 2 );
+            path_board = regex.str( 2 );
 
             type = TYPE_BOARD_MACHI;
         }
@@ -576,9 +575,9 @@
     // vipサービス
     else if( is_vip2ch( url ) ){
 
-        if( regex.exec( "(http://[^/]*)/([^/]*)/$" , url, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.exec( "(https?://[^/]*)(/[^/]*)/$" , url, offset, icase, newline, usemigemo, wchar ) ){
             root = regex.str( 1 );
-            path_board = "/" + regex.str( 2 );
+            path_board = regex.str( 2 );
 
             type = TYPE_BOARD_2CH_COMPATI;
         }
@@ -587,7 +586,7 @@
     // ローカルファイル
     else if( is_local( url ) ){
 
-        root = URL_BOARD_LOCAL;
+        root = "file://";
         path_board = "/local";
 
         type = TYPE_BOARD_LOCAL;
@@ -596,9 +595,9 @@
     // その他は互換型
     else{
 
-        if( regex.exec( "(http://.*)/([^/]*)/([^\\.]+\\.html?)?$" , url, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.exec( "(https?://.*)(/[^/]*)/([^\\.]+\\.html?)?$" , url, offset, icase, newline, usemigemo, wchar ) ){
             root = regex.str( 1 );
-            path_board = "/" + regex.str( 2 );
+            path_board = regex.str( 2 );
 
             type = TYPE_BOARD_2CH_COMPATI;
         }
@@ -618,7 +617,7 @@
     int type = TYPE_BOARD_UNKNOWN;
 
     // 2ch
-    if( ! etc && is_2ch( root ) )
+    if( /*! etc &&*/ is_2ch( root ) )
         type = TYPE_BOARD_2CH;
 
     // JBBS
@@ -653,11 +652,20 @@
     std::cout << "Root::set_board " << url << " " << name << std::endl;
 #endif
 
+    std::string real_url;
     std::string root;
     std::string path_board;
 
+    // scheme省略の場合は補う
+    if( url.compare( 0, 2, "//" ) == 0 ){
+        std::string menu_url = CONFIG::get_url_bbsmenu();
+        size_t pos = menu_url.find("://");
+        if( pos != std::string::npos ) real_url = menu_url.substr( 0, pos + 1 );
+    }
+    real_url += url;
+
     // タイプ判定
-    int type = get_board_type( url, root, path_board, etc );
+    int type = get_board_type( real_url, root, path_board, etc );
     if( type == TYPE_BOARD_UNKNOWN ) return false;
 
     // 移転チェック
@@ -693,7 +701,7 @@
             MISC::ERRMSG( tmp_msg );
 
             const std::string path1 = CACHE::path_board_root_fast( board->url_boardbase() );
-            const std::string path2 = CACHE::path_board_root_fast( url );
+            const std::string path2 = CACHE::path_board_root_fast( real_url );
 
 #ifdef _DEBUG
             std::cout << "path1 = " << path1 << std::endl
@@ -832,19 +840,22 @@
         // キャッシュがある場合はダイアログに表示
         m_move_info += ss.str() + "\n";
 
-        // 移動先に同名のファイルかフォルダ何かあったらリネームしてバックアップをとっておく
-        if( CACHE::file_exists( new_path ) != CACHE::EXIST_ERROR ){
+        if( new_path != old_path ){
 
-            std::string path_tmp = new_path.substr( 0, new_path.length() - 1 ) + "_bk/";
-            if( rename( new_path.c_str(), path_tmp.c_str() ) == 0 ) MISC::MSG( "rename : " +  new_path + " -> " + path_tmp );
-            else MISC::ERRMSG( "can't rename " + new_path + " to " + path_tmp );
-        }
+            // 移動先に同名のファイルかフォルダ何かあったらリネームしてバックアップをとっておく
+            if( CACHE::file_exists( new_path ) != CACHE::EXIST_ERROR ){
+
+                std::string path_tmp = new_path.substr( 0, new_path.length() - 1 ) + "_bk/";
+                if( rename( new_path.c_str(), path_tmp.c_str() ) == 0 ) MISC::MSG( "rename : " +  new_path + " -> " + path_tmp );
+                else MISC::ERRMSG( "can't rename " + new_path + " to " + path_tmp );
+            }
 
-        // キャッシュ移動
-        if( CACHE::mkdir_parent_of_board( new_url ) ){
+            // キャッシュ移動
+            if( CACHE::mkdir_parent_of_board( new_url ) ){
 
-            if( rename( old_path.c_str(), new_path.c_str() ) == 0 ) MISC::MSG( "cache was moved : " +  old_path + " -> " + new_path );
-            else MISC::ERRMSG( "can't move cache from " + old_path + " to " + new_path );
+                if( rename( old_path.c_str(), new_path.c_str() ) == 0 ) MISC::MSG( "cache was moved : " +  old_path + " -> " + new_path );
+                else MISC::ERRMSG( "can't move cache from " + old_path + " to " + new_path );
+            }
         }
 
 #ifdef _DEBUG
@@ -1074,10 +1085,10 @@
             if( it == list_etc.end() ) break;
 
             // basic認証
-            if( regex.exec( "http://([^/]+:[^/]+@)(.+)$" , info.url, offset, icase, newline, usemigemo, wchar ) )
+            if( regex.exec( "(https?://)([^/]+:[^/]+@)(.+)$" , info.url, offset, icase, newline, usemigemo, wchar ) )
             {
-                info.basicauth = regex.str( 1 ).substr( 0, regex.str( 1 ).length() - 1 );
-                info.url = "http://" + regex.str( 2 );
+                info.basicauth = regex.str( 2 ).substr( 0, regex.str( 2 ).length() - 1 );
+                info.url = regex.str( 1 ) + regex.str( 3 );
             }
 
             // board id
@@ -1544,6 +1555,7 @@
     const std::string hostname = MISC::get_hostname( url );
 
     if( ( hostname.find( ".2ch.net" ) != std::string::npos && hostname.find( "info.2ch.net" ) == std::string::npos )
+        || ( hostname.find( ".5ch.net" ) != std::string::npos && hostname.find( "info.5ch.net" ) == std::string::npos )
         || hostname.find( ".bbspink.com" ) != std::string::npos ) return true;
 
     return false;
diff -aurNZ jd-2.8.9-150226/src/dbtree/ruleloader.cpp jd-2.8.9-a171004/src/dbtree/ruleloader.cpp
--- jd-2.8.9-150226/src/dbtree/ruleloader.cpp	2008-11-29 16:56:58.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/ruleloader.cpp	2016-01-20 16:46:14.628713000 +0900
@@ -49,12 +49,6 @@
 }
 
 
-const std::string RuleLoader::get_charset()
-{
-    return DBTREE::board_charset( m_url_boadbase );
-}
-
-
 // ロード用データ作成
 void RuleLoader::create_loaderdata( JDLIB::LOADERDATA& data )
 {
diff -aurNZ jd-2.8.9-150226/src/dbtree/ruleloader.h jd-2.8.9-a171004/src/dbtree/ruleloader.h
--- jd-2.8.9-150226/src/dbtree/ruleloader.h	2007-08-03 23:46:43.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/ruleloader.h	2016-01-20 16:45:27.920713000 +0900
@@ -30,7 +30,6 @@
 
         virtual const std::string get_url();
         virtual const std::string get_path();
-        virtual const std::string get_charset();
 
         // ロード用データ作成
         virtual void create_loaderdata( JDLIB::LOADERDATA& data );
diff -aurNZ jd-2.8.9-150226/src/dbtree/settingloader.cpp jd-2.8.9-a171004/src/dbtree/settingloader.cpp
--- jd-2.8.9-150226/src/dbtree/settingloader.cpp	2010-10-15 00:45:52.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/settingloader.cpp	2016-01-20 16:44:43.632714000 +0900
@@ -12,8 +12,7 @@
 #include "config/globalconf.h"
 
 #include "cache.h"
-
-#define SETTING_TXT "SETTING.TXT"
+#include "global.h"
 
 using namespace DBTREE;
 
@@ -24,7 +23,7 @@
       m_message_count( 0 )
 {
 #ifdef _DEBUG
-    std::cout << "SettingLoader::SettingLoader : " << get_url() << std::endl;
+    std::cout << "SettingLoader::SettingLoader : " << m_url_boadbase << std::endl;
 #endif
 
     set_date_modified( DBTREE::board_get_modified_setting( m_url_boadbase ) );
@@ -34,14 +33,14 @@
 SettingLoader::~SettingLoader()
 {
 #ifdef _DEBUG
-    std::cout << "SettingLoader::~SettingLoader : " << get_url() << std::endl;
+    std::cout << "SettingLoader::~SettingLoader : " << m_url_boadbase << std::endl;
 #endif
 }
 
 
 const std::string SettingLoader::get_url()
 {
-    return m_url_boadbase + SETTING_TXT;
+    return DBTREE::url_settingtxt( m_url_boadbase );
 }
 
 
@@ -51,12 +50,6 @@
 }
 
 
-const std::string SettingLoader::get_charset()
-{
-    return DBTREE::board_charset( m_url_boadbase );
-}
-
-
 // ロード用データ作成
 void SettingLoader::create_loaderdata( JDLIB::LOADERDATA& data )
 {
@@ -89,6 +82,12 @@
     m_line_number = cf.get_option_int( "BBS_LINE_NUMBER", 0, 0, 8192 );
     m_message_count = cf.get_option_int( "BBS_MESSAGE_COUNT", 0, 0, 81920 );
     m_unicode = cf.get_option_str( "BBS_UNICODE", "" );
+    const int num_thread_stop = cf.get_option_int( "BBS_THREAD_STOP", 0, 0, MAX_RESNUMBER );
+    if( num_thread_stop ){
+        const int max_res = DBTREE::board_get_number_max_res( m_url_boadbase );
+        if( ! max_res && max_res != num_thread_stop )
+            DBTREE::board_set_number_max_res( m_url_boadbase, num_thread_stop );
+    }
     DBTREE::board_set_modified_setting( m_url_boadbase, get_date_modified() );
 
 #ifdef _DEBUG
diff -aurNZ jd-2.8.9-150226/src/dbtree/settingloader.h jd-2.8.9-a171004/src/dbtree/settingloader.h
--- jd-2.8.9-150226/src/dbtree/settingloader.h	2010-10-15 00:45:52.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/settingloader.h	2016-02-16 00:33:09.113012000 +0900
@@ -10,6 +10,8 @@
 
 #include <string>
 
+#define SETTING_TXT "SETTING.TXT"
+
 namespace JDLIB
 {
     class LOADERDATA;
@@ -19,7 +21,6 @@
 {
     class SettingLoader : public SKELETON::TextLoader
     {
-        bool m_parsed;
         std::string m_url_boadbase;
 
         // デフォルト名無し
@@ -48,7 +49,6 @@
 
         virtual const std::string get_url();
         virtual const std::string get_path();
-        virtual const std::string get_charset();
 
         // ロード用データ作成
         virtual void create_loaderdata( JDLIB::LOADERDATA& data );
diff -aurNZ jd-2.8.9-150226/src/dbtree/spchar_decoder.cpp jd-2.8.9-a171004/src/dbtree/spchar_decoder.cpp
--- jd-2.8.9-150226/src/dbtree/spchar_decoder.cpp	2014-11-07 22:24:30.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/spchar_decoder.cpp	2016-11-11 23:03:28.205592000 +0900
@@ -7,7 +7,9 @@
 #include "spchar_tbl.h"
 #include "node.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
+#include "config/globalconf.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -48,21 +50,58 @@
 
     if( only_check ) return ret;
 
-    const int num = MISC::decode_spchar_number( in_char, offset, lng );
+    int num = MISC::decode_spchar_number( in_char, offset, lng );
+
+    // 特殊条件を処理
+    if( num >= 0x80 && num <= 0x9F ) num = charref_tbl[ num - 0x80 ];
+    else if( num == 0 ||  num > 0x10FFFF ) num = UCS_REPLACE;
+    else if( num >= 0xD800 && num < 0xE000 ){
+
+        // 間違ったエンコードで壊れた追加面の文字を修復する
+        if( !CONFIG::get_correct_character_reference() ) num = UCS_REPLACE;
+        else {
+            const char *char_low = in_char + offset + lng + 1;
+            int offset_low;
+            int lng_low = MISC::spchar_number_ln( char_low, offset_low );
+            if( lng_low == -1 ) num = UCS_REPLACE;
+            else{
+                const int num_low = MISC::decode_spchar_number( char_low, offset_low, lng_low );
+                if( num < 0xDC01 && num_low >= 0xDC00 && num_low < 0xE000 ){
+                    num = 0x10000 + ( num - 0xD800 ) * 0x400 + ( num_low - 0xDC00 );
+                    offset += 1 + offset_low + lng_low;
+                }
+                else num = UCS_REPLACE;
+            }
+        }
+    }
 
     switch( num ){
 
-        //zwnj,zwj,lrm,rlm は今のところ無視(zwspにする)
+        //lfはspにする
+        case UCS_SP:
+        case UCS_LF:
+            ret = DBTREE::NODE_SP;
+            break;
+
+        case UCS_HT:
+            ret = DBTREE::NODE_HTAB;
+            break;
+
+        //zwnj,zwj,lrm,rlm,lre,rle,lro.rlo は今のところ無視
         case UCS_ZWSP:
-        case UCS_ZWNJ:
-        case UCS_ZWJ:
-        case UCS_LRM:
-        case UCS_RLM:
+//        case UCS_ZWNJ:
+//        case UCS_ZWJ:
+//        case UCS_LRM:
+//        case UCS_RLM:
+        case UCS_CR: // CRを無視
+        case UCS_FF: // FFを無視
+        case UCS_LS: // LSを無視
+        case UCS_PS: // PSを無視
             ret = DBTREE::NODE_ZWSP;
             break;
 
         default:
-            n_out = MISC::ucs2toutf8( num, out_char );
+            n_out = MISC::cptoutf8( num, out_char );
             if( ! n_out ) return DBTREE::NODE_NONE;
     }
 
@@ -103,28 +142,25 @@
     int ret = DBTREE::NODE_TEXT;
     n_in = n_out = 0;
 
-    int i = 0;
-    for(;;){
+    const char ch = in_char[ 1 ];
+    UCSTBL const *tbl;
 
-        const int ucs = ucstbl[ i ].ucs;
-        if( ! ucs ) break;
-        if( in_char[ 1 ] == ucstbl[ i ].str[ 0 ] ){
+    if( ch >= 'a' && ch <= 'z' ) tbl = ucstbl_small[ ch - 'a' ];
+    else if( ch >= 'A' && ch <= 'Z' ) tbl = ucstbl_large[ ch - 'A' ];
+    else return DBTREE::NODE_NONE;
 
-            if( check_spchar( in_char +1, ucstbl[ i ].str ) ){
+    for( int ucs = tbl->ucs; ucs != 0; ucs = ( ++tbl )->ucs ){
+        if( check_spchar( in_char + 1, tbl->str ) ){
 
-                if( only_check ) return ret;
+            if( only_check ) return ret;
 
-                n_in = strlen( ucstbl[ i ].str ) +1;
+            n_in = strlen( tbl->str ) + 1; // 先頭の '&' の分を+1する
 
-                // zwnj, zwj, lrm, rlm は今のところ無視する(zwspにする)
-                if( ucs >= UCS_ZWSP && ucs <= UCS_RLM ) ret = DBTREE::NODE_ZWSP;
-                else n_out = MISC::ucs2toutf8( ucs, out_char );
+            if( ucs == UCS_ZWSP ) ret = DBTREE::NODE_ZWSP;
+            else n_out = MISC::cptoutf8( ucs, out_char );
 
-                break;
-            }
+            break;
         }
-
-        ++i;
     }
 
     if( !n_in ) ret = DBTREE::NODE_NONE;
diff -aurNZ jd-2.8.9-150226/src/dbtree/spchar_tbl.h jd-2.8.9-a171004/src/dbtree/spchar_tbl.h
--- jd-2.8.9-150226/src/dbtree/spchar_tbl.h	2010-10-24 01:04:24.000000000 +0900
+++ jd-2.8.9-a171004/src/dbtree/spchar_tbl.h	2016-11-11 22:55:26.933599000 +0900
@@ -8,282 +8,515 @@
 
 struct UCSTBL
 {
-    int ucs;
-    char str[ 256 ];
+    unsigned short ucs;
+    char str[ 12 ];
 };
 
 
-UCSTBL ucstbl[] = {
+static UCSTBL const ucstbl_empty[] = {
+    { 0, "" }
+};
 
-    { 34, "quot;" },
-    { 38, "amp;" },
-    { 60, "lt;" },
-    { 62, "gt;" },
-
-    { 32, "nbsp;" },
-//    { 160, "nbsp;" }, // 正しくはこちら
-
-    { 161, "iexcl;" },
-    { 162, "cent;" },
-    { 163, "pound;" },
-    { 164, "curren;" },
-    { 165, "yen;" },
-    { 166, "brvbar;" },
-    { 167, "sect;" },
-    { 168, "uml;" },
-    { 169, "copy;" },
-    { 170, "ordf;" },
-    { 171, "laquo;" },
-    { 172, "not;" },
-    { 173, "shy;" },
-    { 174, "reg;" },
-    { 175, "macr;" },
-    { 176, "deg;" },
-    { 177, "plusmn;" },
-    { 178, "sup2;" },
-    { 179, "sup3;" },
-    { 180, "acute;" },
-    { 181, "micro;" },
-    { 182, "para;" },
-    { 183, "middot;" },
-    { 184, "cedil;" },
-    { 185, "sup1;" },
-    { 186, "ordm;" },
-    { 187, "raquo;" },
-    { 188, "frac14;" },
-    { 189, "frac12;" },
-    { 190, "frac34;" },
-    { 191, "iquest;" },
-    { 192, "Agrave;" },
-    { 193, "Aacute;" },
-    { 194, "Acirc;" },
-    { 195, "Atilde;" },
-    { 196, "Auml;" },
-    { 197, "Aring;" },
-    { 198, "AElig;" },
-    { 199, "Ccedil;" },
-    { 200, "Egrave;" },
-    { 201, "Eacute;" },
-    { 202, "Ecirc;" },
-    { 203, "Euml;" },
-    { 204, "Igrave;" },
-    { 205, "Iacute;" },
-    { 206, "Icirc;" },
-    { 207, "Iuml;" },
-    { 208, "ETH;" },
-    { 209, "Ntilde;" },
-    { 210, "Ograve;" },
-    { 211, "Oacute;" },
-    { 212, "Ocirc;" },
-    { 213, "Otilde;" },
-    { 214, "Ouml;" },
-    { 215, "times;" },
-    { 216, "Oslash;" },
-    { 217, "Ugrave;" },
-    { 218, "Uacute;" },
-    { 219, "Ucirc;" },
-    { 220, "Uuml;" },
-    { 221, "Yacute;" },
-    { 222, "THORN;" },
-    { 223, "szlig;" },
-    { 224, "agrave;" },
-    { 225, "aacute;" },
-    { 226, "acirc;" },
-    { 227, "atilde;" },
-    { 228, "auml;" },
-    { 229, "aring;" },
-    { 230, "aelig;" },
-    { 231, "ccedil;" },
-    { 232, "egrave;" },
-    { 233, "eacute;" },
-    { 234, "ecirc;" },
-    { 235, "euml;" },
-    { 236, "igrave;" },
-    { 237, "iacute;" },
-    { 238, "icirc;" },
-    { 239, "iuml;" },
-    { 240, "eth;" },
-    { 241, "ntilde;" },
-    { 242, "ograve;" },
-    { 243, "oacute;" },
-    { 244, "ocirc;" },
-    { 245, "otilde;" },
-    { 246, "ouml;" },
-    { 247, "divide;" },
-    { 248, "oslash;" },
-    { 249, "ugrave;" },
-    { 250, "uacute;" },
-    { 251, "ucirc;" },
-    { 252, "uuml;" },
-    { 253, "yacute;" },
-    { 254, "thorn;" },
-    { 255, "yuml;" },
-    { 338, "OElig;" },
-    { 339, "oelig;" },
-    { 352, "Scaron;" },
-    { 353, "scaron;" },
-    { 376, "Yuml;" },
-    { 402, "fnof;" },
-    { 710, "circ;" },
-    { 732, "tilde;" },
-    { 913, "Alpha;" },
-    { 914, "Beta;" },
-    { 915, "Gamma;" },
-    { 916, "Delta;" },
-    { 917, "Epsilon;" },
-    { 918, "Zeta;" },
-    { 919, "Eta;" },
-    { 920, "Theta;" },
-    { 921, "Iota;" },
-    { 922, "Kappa;" },
-    { 923, "Lambda;" },
-    { 924, "Mu;" },
-    { 925, "Nu;" },
-    { 926, "Xi;" },
-    { 927, "Omicron;" },
-    { 928, "Pi;" },
-    { 929, "Rho;" },
-    { 931, "Sigma;" },
-    { 932, "Tau;" },
-    { 933, "Upsilon;" },
-    { 934, "Phi;" },
-    { 935, "Chi;" },
-    { 936, "Psi;" },
-    { 937, "Omega;" },
-    { 945, "alpha;" },
-    { 946, "beta;" },
-    { 947, "gamma;" },
-    { 948, "delta;" },
-    { 949, "epsilon;" },
-    { 950, "zeta;" },
-    { 951, "eta;" },
-    { 952, "theta;" },
-    { 953, "iota;" },
-    { 954, "kappa;" },
-    { 955, "lambda;" },
-    { 956, "mu;" },
-    { 957, "nu;" },
-    { 958, "xi;" },
-    { 959, "omicron;" },
-    { 960, "pi;" },
-    { 961, "rho;" },
-    { 962, "sigmaf;" },
-    { 963, "sigma;" },
-    { 964, "tau;" },
-    { 965, "upsilon;" },
-    { 966, "phi;" },
-    { 967, "chi;" },
-    { 968, "psi;" },
-    { 969, "omega;" },
-    { 977, "thetasym;" },
-    { 978, "upsih;" },
-    { 982, "piv;" },
+static UCSTBL const ucstbl_a[] = {
+    { 38,   "amp;" },
+    { 180,  "acute;" },
+    { 224,  "agrave;" },
+    { 225,  "aacute;" },
+    { 226,  "acirc;" },
+    { 227,  "atilde;" },
+    { 228,  "auml;" },
+    { 229,  "aring;" },
+    { 230,  "aelig;" },
+    { 945,  "alpha;" },
+    { 8501, "alefsym;" },
+    { 8736, "ang;" },
+    { 8743, "and;" },
+    { 8776, "asymp;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_b[] = {
+    { 166,  "brvbar;" },
+    { 946,  "beta;" },
+    { 8222, "bdquo;" },
+    { 8226, "bull;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_c[] = {
+    { 162,  "cent;" },
+    { 164,  "curren;" },
+    { 169,  "copy;" },
+    { 184,  "cedil;" },
+    { 231,  "ccedil;" },
+    { 710,  "circ;" },
+    { 967,  "chi;" },
+    { 8629, "crarr;" },
+    { 8745, "cap;" },
+    { 8746, "cup;" },
+    { 8773, "cong;" },
+    { 9827, "clubs;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_d[] = {
+    { 176,  "deg;" },
+    { 247,  "divide;" },
+    { 948,  "delta;" },
+    { 8224, "dagger;" },
+    { 8595, "darr;" },
+    { 8659, "dArr;" },
+    { 9830, "diams;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_e[] = {
+    { 232,  "egrave;" },
+    { 233,  "eacute;" },
+    { 234,  "ecirc;" },
+    { 235,  "euml;" },
+    { 240,  "eth;" },
+    { 949,  "epsilon;" },
+    { 951,  "eta;" },
     { 8194, "ensp;" },
     { 8195, "emsp;" },
-    { 8201, "thinsp;" },
-    { 8203, "zwsp;" },
-    { 8204, "zwnj;" },
-    { 8205, "zwj;" },
+    { 8364, "euro;" },
+    { 8707, "exist;" },
+    { 8709, "empty;" },
+    { 8801, "equiv;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_f[] = {
+    { 188,  "frac14;" },
+    { 189,  "frac12;" },
+    { 190,  "frac34;" },
+    { 402,  "fnof;" },
+    { 8260, "frasl;" },
+    { 8704, "forall;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_g[] = {
+    { 62,   "gt;" },
+    { 947,  "gamma;" },
+    { 8805, "ge;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_h[] = {
+    { 8230, "hellip;" },
+    { 8596, "harr;" },
+    { 8660, "hArr;" },
+    { 9829, "hearts;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_i[] = {
+    { 161,  "iexcl;" },
+    { 191,  "iquest;" },
+    { 236,  "igrave;" },
+    { 237,  "iacute;" },
+    { 238,  "icirc;" },
+    { 239,  "iuml;" },
+    { 953,  "iota;" },
+    { 8465, "image;" },
+    { 8712, "isin;" },
+    { 8734, "infin;" },
+    { 8747, "int;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_k[] = {
+    { 954,  "kappa;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_l[] = {
+    { 60,   "lt;" },
+    { 171,  "laquo;" },
+    { 955,  "lambda;" },
     { 8206, "lrm;" },
-    { 8207, "rlm;" },
-    { 8211, "ndash;" },
-    { 8212, "mdash;" },
     { 8216, "lsquo;" },
-    { 8217, "rsquo;" },
-    { 8218, "sbquo;" },
     { 8220, "ldquo;" },
-    { 8221, "rdquo;" },
-    { 8222, "bdquo;" },
-    { 8224, "dagger;" },
-    { 8225, "Dagger;" },
-    { 8226, "bull;" },
-    { 8230, "hellip;" },
-    { 8240, "permil;" },
-    { 8242, "prime;" },
-    { 8243, "Prime;" },
     { 8249, "lsaquo;" },
-    { 8250, "rsaquo;" },
-    { 8254, "oline;" },
-    { 8260, "frasl;" },
-    { 8364, "euro;" },
-    { 8465, "image;" },
-    { 8472, "weierp;" },
-    { 8476, "real;" },
-    { 8482, "trade;" },
-    { 8501, "alefsym;" },
     { 8592, "larr;" },
-    { 8593, "uarr;" },
-    { 8594, "rarr;" },
-    { 8595, "darr;" },
-    { 8596, "harr;" },
-    { 8629, "crarr;" },
     { 8656, "lArr;" },
-    { 8657, "uArr;" },
-    { 8658, "rArr;" },
-    { 8659, "dArr;" },
-    { 8660, "hArr;" },
-    { 8704, "forall;" },
-    { 8706, "part;" },
-    { 8707, "exist;" },
-    { 8709, "empty;" },
+    { 8727, "lowast;" },
+    { 8804, "le;" },
+    { 8968, "lceil;" },
+    { 8970, "lfloor;" },
+    { 9001, "lang;" },
+    { 9674, "loz;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_m[] = {
+    { 175,  "macr;" },
+    { 181,  "micro;" },
+    { 183,  "middot;" },
+    { 956,  "mu;" },
+    { 8212, "mdash;" },
+    { 8722, "minus;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_n[] = {
+    { 160,  "nbsp;" },
+    { 172,  "not;" },
+    { 241,  "ntilde;" },
+    { 957,  "nu;" },
+    { 8211, "ndash;" },
     { 8711, "nabla;" },
-    { 8712, "isin;" },
     { 8713, "notin;" },
     { 8715, "ni;" },
-    { 8719, "prod;" },
-    { 8721, "sum;" },
-    { 8722, "minus;" },
-    { 8727, "lowast;" },
-    { 8730, "radic;" },
-    { 8733, "prop;" },
-    { 8734, "infin;" },
-    { 8736, "ang;" },
-    { 8743, "and;" },
-    { 8744, "or;" },
-    { 8745, "cap;" },
-    { 8746, "cup;" },
-    { 8747, "int;" },
-    { 8756, "there4;" },
-    { 8764, "sim;" },
-    { 8773, "cong;" },
-    { 8776, "asymp;" },
     { 8800, "ne;" },
-    { 8801, "equiv;" },
-    { 8804, "le;" },
-    { 8805, "ge;" },
-    { 8834, "sub;" },
-    { 8835, "sup;" },
     { 8836, "nsub;" },
-    { 8838, "sube;" },
-    { 8839, "supe;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_o[] = {
+    { 170,  "ordf;" },
+    { 186,  "ordm;" },
+    { 242,  "ograve;" },
+    { 243,  "oacute;" },
+    { 244,  "ocirc;" },
+    { 245,  "otilde;" },
+    { 246,  "ouml;" },
+    { 248,  "oslash;" },
+    { 339,  "oelig;" },
+    { 959,  "omicron;" },
+    { 969,  "omega;" },
+    { 8254, "oline;" },
+    { 8744, "or;" },
     { 8853, "oplus;" },
     { 8855, "otimes;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_p[] = {
+    { 163,  "pound;" },
+    { 177,  "plusmn;" },
+    { 182,  "para;" },
+    { 960,  "pi;" },
+    { 966,  "phi;" },
+    { 968,  "psi;" },
+    { 982,  "piv;" },
+    { 8240, "permil;" },
+    { 8242, "prime;" },
+    { 8706, "part;" },
+    { 8719, "prod;" },
+    { 8733, "prop;" },
     { 8869, "perp;" },
-    { 8901, "sdot;" },
-    { 8968, "lceil;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_q[] = {
+    { 34,   "quot;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_r[] = {
+    { 174,  "reg;" },
+    { 187,  "raquo;" },
+    { 961,  "rho;" },
+    { 8207, "rlm;" },
+    { 8217, "rsquo;" },
+    { 8221, "rdquo;" },
+    { 8250, "rsaquo;" },
+    { 8476, "real;" },
+    { 8594, "rarr;" },
+    { 8658, "rArr;" },
+    { 8730, "radic;" },
     { 8969, "rceil;" },
-    { 8970, "lfloor;" },
     { 8971, "rfloor;" },
-    { 9001, "lang;" },
     { 9002, "rang;" },
-    { 9674, "loz;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_s[] = {
+    { 167,  "sect;" },
+    { 173,  "shy;" },
+    { 178,  "sup2;" },
+    { 179,  "sup3;" },
+    { 185,  "sup1;" },
+    { 223,  "szlig;" },
+    { 353,  "scaron;" },
+    { 962,  "sigmaf;" },
+    { 963,  "sigma;" },
+    { 8218, "sbquo;" },
+    { 8721, "sum;" },
+    { 8764, "sim;" },
+    { 8834, "sub;" },
+    { 8835, "sup;" },
+    { 8838, "sube;" },
+    { 8839, "supe;" },
+    { 8901, "sdot;" },
     { 9824, "spades;" },
-    { 9827, "clubs;" },
-    { 9829, "hearts;" },
-    { 9830, "diams;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_t[] = {
+    { 215,  "times;" },
+    { 254,  "thorn;" },
+    { 732,  "tilde;" },
+    { 952,  "theta;" },
+    { 964,  "tau;" },
+    { 977,  "thetasym;" },
+    { 8201, "thinsp;" },
+    { 8482, "trade;" },
+    { 8756, "there4;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_u[] = {
+    { 168,  "uml;" },
+    { 249,  "ugrave;" },
+    { 250,  "uacute;" },
+    { 251,  "ucirc;" },
+    { 252,  "uuml;" },
+    { 965,  "upsilon;" },
+    { 978,  "upsih;" },
+    { 8593, "uarr;" },
+    { 8657, "uArr;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_w[] = {
+    { 8472, "weierp;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_x[] = {
+    { 958,  "xi;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_y[] = {
+    { 165,  "yen;" },
+    { 253,  "yacute;" },
+    { 255,  "yuml;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_z[] = {
+    { 950,  "zeta;" },
+    { 8203, "zwsp;" },
+    { 8204, "zwnj;" },
+    { 8205, "zwj;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_A[] = {
+    { 192,  "Agrave;" },
+    { 193,  "Aacute;" },
+    { 194,  "Acirc;" },
+    { 195,  "Atilde;" },
+    { 196,  "Auml;" },
+    { 197,  "Aring;" },
+    { 198,  "AElig;" },
+    { 913,  "Alpha;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_B[] = {
+    { 914,  "Beta;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_C[] = {
+    { 199,  "Ccedil;" },
+    { 935,  "Chi;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_D[] = {
+    { 916,  "Delta;" },
+    { 8225, "Dagger;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_E[] = {
+    { 200,  "Egrave;" },
+    { 201,  "Eacute;" },
+    { 202,  "Ecirc;" },
+    { 203,  "Euml;" },
+    { 208,  "ETH;" },
+    { 917,  "Epsilon;" },
+    { 919,  "Eta;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_G[] = {
+    { 915,  "Gamma;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_I[] = {
+    { 204,  "Igrave;" },
+    { 205,  "Iacute;" },
+    { 206,  "Icirc;" },
+    { 207,  "Iuml;" },
+    { 921,  "Iota;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_K[] = {
+    { 922,  "Kappa;" },
+    { 0, "" }
+};
 
-    { 0, "" } // 終端
+static UCSTBL const ucstbl_L[] = {
+    { 923,  "Lambda;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_M[] = {
+    { 924,  "Mu;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_N[] = {
+    { 209,  "Ntilde;" },
+    { 925,  "Nu;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_O[] = {
+    { 210,  "Ograve;" },
+    { 211,  "Oacute;" },
+    { 212,  "Ocirc;" },
+    { 213,  "Otilde;" },
+    { 214,  "Ouml;" },
+    { 216,  "Oslash;" },
+    { 338,  "OElig;" },
+    { 927,  "Omicron;" },
+    { 937,  "Omega;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_P[] = {
+    { 928,  "Pi;" },
+    { 934,  "Phi;" },
+    { 936,  "Psi;" },
+    { 8243, "Prime;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_R[] = {
+    { 929,  "Rho;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_S[] = {
+    { 352,  "Scaron;" },
+    { 931,  "Sigma;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_T[] = {
+    { 222,  "THORN;" },
+    { 920,  "Theta;" },
+    { 932,  "Tau;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_U[] = {
+    { 217,  "Ugrave;" },
+    { 218,  "Uacute;" },
+    { 219,  "Ucirc;" },
+    { 220,  "Uuml;" },
+    { 933,  "Upsilon;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_X[] = {
+    { 926,  "Xi;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_Y[] = {
+    { 221,  "Yacute;" },
+    { 376,  "Yuml;" },
+    { 0, "" }
+};
+
+static UCSTBL const ucstbl_Z[] = {
+    { 918, "Zeta;" },
+    { 0, "" }
+};
+
+static UCSTBL const * const ucstbl_small[] = {
+    ucstbl_a, ucstbl_b, ucstbl_c, ucstbl_d, ucstbl_e,
+    ucstbl_f, ucstbl_g, ucstbl_h, ucstbl_i, ucstbl_empty,
+    ucstbl_k, ucstbl_l, ucstbl_m, ucstbl_n, ucstbl_o,
+    ucstbl_p, ucstbl_q, ucstbl_r, ucstbl_s, ucstbl_t,
+    ucstbl_u, ucstbl_empty, ucstbl_w, ucstbl_x, ucstbl_y,
+    ucstbl_z
+};
+
+static UCSTBL const * const ucstbl_large[] = {
+    ucstbl_A, ucstbl_B, ucstbl_C, ucstbl_D, ucstbl_E,
+    ucstbl_empty, ucstbl_G, ucstbl_empty, ucstbl_I, ucstbl_empty,
+    ucstbl_K, ucstbl_L, ucstbl_M, ucstbl_N, ucstbl_O,
+    ucstbl_P, ucstbl_empty, ucstbl_R, ucstbl_S, ucstbl_T,
+    ucstbl_U, ucstbl_empty, ucstbl_empty, ucstbl_X, ucstbl_Y,
+    ucstbl_Z
 };
 
 
 enum
 {
-    UCS_ZWSP    = 8203,
-    UCS_ZWNJ    = 8204,
-    UCS_ZWJ     = 8205,
-    UCS_LRM     = 8206,
-    UCS_RLM     = 8207,
+    UCS_HT      = 9,
+    UCS_LF      = 10,
+    UCS_FF      = 12,
+    UCS_CR      = 13,
+    UCS_SP      = 32,
+    UCS_NBSP    = 160,
+    UCS_ZWSP    = 0x200B,
+    UCS_ZWNJ    = 0x200C,
+    UCS_ZWJ     = 0x200D,
+    UCS_LRM     = 0x200E,
+    UCS_RLM     = 0x200F,
+    UCS_LS      = 0x2028,
+    UCS_PS      = 0x2029,
+    UCS_REPLACE = 0xFFFD
 };
 
 
+static unsigned short const charref_tbl [] = {
+    0x20AC, //	EURO SIGN (€)
+    0x81,   //	U+0081 (HOP)
+    0x201A, //	SINGLE LOW-9 QUOTATION MARK (‚)
+    0x0192, //	LATIN SMALL LETTER F WITH HOOK (ƒ)
+    0x201E, //	DOUBLE LOW-9 QUOTATION MARK („)
+    0x2026, //	HORIZONTAL ELLIPSIS (…)
+    0x2020, //	DAGGER (†)
+    0x2021, //	DOUBLE DAGGER (‡)
+    0x02C6, //	MODIFIER LETTER CIRCUMFLEX ACCENT (ˆ)
+    0x2030, //	PER MILLE SIGN (‰)
+    0x0160, //	LATIN CAPITAL LETTER S WITH CARON (Š)
+    0x2039, //	SINGLE LEFT-POINTING ANGLE QUOTATION MARK (‹)
+    0x0152, //	LATIN CAPITAL LIGATURE OE (Œ)
+    0x8D,   //	U+008D (RI)
+    0x017D, //	LATIN CAPITAL LETTER Z WITH CARON (Ž)
+    0x8F,   //	U+008F (SS3)
+    0x90,   //	U+0090 (DCS)
+    0x2018, //	LEFT SINGLE QUOTATION MARK (‘)
+    0x2019, //	RIGHT SINGLE QUOTATION MARK (’)
+    0x201C, //	LEFT DOUBLE QUOTATION MARK (“)
+    0x201D, //	RIGHT DOUBLE QUOTATION MARK (”)
+    0x2022, //	BULLET (•)
+    0x2013, //	EN DASH (–)
+    0x2014, //	EM DASH (—)
+    0x02DC, //	SMALL TILDE (˜)
+    0x2122, //	TRADE MARK SIGN (™)
+    0x0161, //	LATIN SMALL LETTER S WITH CARON (š)
+    0x203A, //	SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (›)
+    0x0153, //	LATIN SMALL LIGATURE OE (œ)
+    0x9D,   //	U+009D (OSC)
+    0x017E, //	LATIN SMALL LETTER Z WITH CARON (ž)
+    0x0178, //	LATIN CAPITAL LETTER Y WITH DIAERESIS (Ÿ)
+};
+
 #endif
diff -aurNZ jd-2.8.9-150226/src/environment.cpp jd-2.8.9-a171004/src/environment.cpp
--- jd-2.8.9-150226/src/environment.cpp	2014-01-26 00:08:56.000000000 +0900
+++ jd-2.8.9-a171004/src/environment.cpp	2017-04-03 19:12:19.299210000 +0900
@@ -1,8 +1,10 @@
 // License GPL2
 
 #ifdef _WIN32
+#  ifndef WINVER
 // require Windows XP SP2 or Server 2003 SP1 for GetNativeSystemInfo, KEY_WOW64_64KEY
-#define WINVER 0x0502
+#    define WINVER 0x0502
+#  endif
 #endif
 
 //#define _DEBUG
@@ -27,6 +29,14 @@
 #include <windows.h>
 #endif
 
+#if defined(USE_GNUTLS)
+#  include <gnutls/gnutls.h>
+#elif defined(USE_OPENSSL)
+#  include <openssl/ssl.h>
+#elif defined(USE_NSS)
+#  include <nss/nss.h>
+#endif
+
 std::string ENVIRONMENT::get_jdcomments(){ return std::string( JDCOMMENT ); }
 std::string ENVIRONMENT::get_jdcopyright(){ return std::string( JDCOPYRIGHT ); }
 std::string ENVIRONMENT::get_jdbbs(){ return std::string( JDBBS ); }
@@ -238,12 +248,20 @@
             // OS architecture
             if (osvi.dwMajorVersion >= 5)
             {
-                SYSTEM_INFO sysi;
-                GetNativeSystemInfo(&sysi);
-                if (sysi.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
-                    vstr << " x64";
-                else if (sysi.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
-                    vstr << " ia64";
+                void (CALLBACK *pfn)(LPSYSTEM_INFO);
+
+                (*(FARPROC*)&pfn) = GetProcAddress( GetModuleHandle( "Kernel32.dll" ),
+                                                    "GetNativeSystemInfo" );
+                if( pfn != NULL )
+                {
+                    SYSTEM_INFO sysi;
+
+                    pfn( &sysi );
+                    if (sysi.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+                        vstr << " x64";
+                    else if (sysi.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
+                        vstr << " ia64";
+                }
             }
             break;
         case VER_PLATFORM_WIN32_WINDOWS:
@@ -419,11 +437,12 @@
         ( strlen( machine ) != 4
           || ! ( machine[0] == 'i'
                && machine[1] >= '3' && machine[1] <= '6'
-               && machine[2] == '8' && machine[3] == '6' ) ) )
+               && machine[2] == '8' && machine[3] == '6' ) ) &&
+        dist_name.find( machine, 0 ) == std::string::npos )
     {
-        const std::string arch = "(" + std::string( machine ) + ")";
-
-        if ( dist_name.find(arch, 0) == std::string::npos ) dist_name.append( " " + arch);
+        dist_name += " (";
+        dist_name += machine;
+        dist_name += ")";
     }
 
     free( uts );
@@ -436,20 +455,33 @@
 }
 
 
+static const char* const tbl_desktop[] = {
+    "GNOME", "XFCE", "KDE", "LXDE", "UNITY", "CINNAMON", "MATE",
+    "BUDGIE", "PANTHEON", "ENLIGHTENMENT", "(unknown)" };
+
 //
 // WM 判定
 // TODO: 環境変数で判定できない場合の判定方法を考える
 //
 const int ENVIRONMENT::get_wm()
 {
+#ifndef _WIN32
     if( window_manager != WM_UNKNOWN ) return window_manager;
 
-    const std::string str_wm = MISC::getenv_limited( "DESKTOP_SESSION", 5 );
+    const char* const envver[] = { "DESKTOP_SESSION", "XDG_CURRENT_DESKTOP" };
+
+    for( size_t j=0; j<(sizeof envver/sizeof *envver); j++){
 
-    if( str_wm.find( "xfce" ) != std::string::npos ) window_manager = WM_XFCE;
-    else if( str_wm.find( "gnome" ) != std::string::npos ) window_manager = WM_GNOME;
-    else if( str_wm.find( "kde" ) != std::string::npos ) window_manager = WM_KDE;
-    else if( str_wm.find( "LXDE" ) != std::string::npos ) window_manager = WM_LXDE;
+        std::string str_wm = MISC::toupper_str( MISC::getenv_limited( envver[ j ], 20 ) );
+
+        if( !str_wm.empty() ){
+            for( int i = 0; i < WM_UNKNOWN; ++i ){
+                if( str_wm.find( tbl_desktop[ i ] ) != std::string::npos ){
+                    window_manager = i;
+                }
+            }
+        }
+    }
 
     if( window_manager == WM_UNKNOWN )
     {
@@ -463,6 +495,7 @@
             if( str_wm == "true" ) window_manager = WM_KDE;
         }
     }
+#endif
 
     return window_manager;
 }
@@ -477,18 +510,12 @@
 
 #ifdef _WIN32
 #ifdef __MINGW32__
-    return std::string( "build by mingw32" );
+    desktop = "build by mingw32";
 #else
-    return std::string( "build with _WIN32" );
+    desktop = "build with _WIN32";
 #endif
 #else
-    switch( get_wm() )
-    {
-        case WM_GNOME : desktop = "GNOME"; break;
-        case WM_XFCE  : desktop = "XFCE";  break;
-        case WM_KDE   : desktop = "KDE";   break;
-        case WM_LXDE  : desktop = "LXDE";  break;
-    }
+    desktop = tbl_desktop[ get_wm() ];
 #endif
 
     return desktop;
@@ -524,6 +551,28 @@
 
 
 //
+// TLSライブラリのバージョンを取得
+//
+std::string ENVIRONMENT::get_tlslib_version()
+{
+    std::string version;
+
+#if defined(USE_GNUTLS)
+    version = "GnuTLS ";
+    version +=gnutls_check_version(0);
+#elif defined(USE_OPENSSL)
+    version = SSLeay_version(SSLEAY_VERSION);
+#elif defined(USE_NSS)
+    version = "NSS ";
+    version += NSS_GetVersion();
+#endif
+
+    return version;
+}
+
+
+
+//
 // 動作環境を取得
 //
 std::string ENVIRONMENT::get_jdinfo()
@@ -556,7 +605,8 @@
     "[パッケージ] " << "バイナリ/ソース( <配布元> )" << "\n" <<
     "[ DE/WM ] " << desktop << "\n" <<
     "[ gtkmm  ] " << get_gtkmm_version() << "\n" <<
-    "[ glibmm  ] " << get_glibmm_version()<< "\n" <<
+    "[ glibmm  ] " << get_glibmm_version() << "\n" <<
+    "[ TLS lib ] " << get_tlslib_version() << "\n" <<
 #ifdef CONFIGURE_ARGS
     "[オプション ] " << get_configure_args( CONFIGURE_OMITTED_MULTILINE ) << "\n" <<
 #endif
diff -aurNZ jd-2.8.9-150226/src/environment.h jd-2.8.9-a171004/src/environment.h
--- jd-2.8.9-150226/src/environment.h	2013-01-27 23:23:42.000000000 +0900
+++ jd-2.8.9-a171004/src/environment.h	2017-04-03 19:06:43.167215000 +0900
@@ -1,6 +1,6 @@
 // License GPL2
 
-#ifndef _ENVIRINMENT_H
+#ifndef _ENVIRONMENT_H
 #define _ENVIRONMENT_H
 
 #include <string>
@@ -14,6 +14,12 @@
         WM_XFCE,
         WM_KDE,
         WM_LXDE,
+        WM_UNITY,
+        WM_CINNAMON,
+        WM_MATE,
+        WM_BUDGIE,
+        WM_PANTHEON,
+        WM_ENLIGHTENMENT,
         WM_UNKNOWN
     };
 
@@ -40,6 +46,7 @@
     std::string get_wm_str();
 	std::string get_gtkmm_version();
 	std::string get_glibmm_version();
+    std::string get_tlslib_version();
     std::string get_jdinfo();
 }
 
diff -aurNZ jd-2.8.9-150226/src/fontcolorpref.cpp jd-2.8.9-a171004/src/fontcolorpref.cpp
--- jd-2.8.9-150226/src/fontcolorpref.cpp	2013-07-28 14:28:10.000000000 +0900
+++ jd-2.8.9-a171004/src/fontcolorpref.cpp	2016-01-16 19:29:51.043586000 +0900
@@ -190,7 +190,7 @@
 
     m_liststore_color = Gtk::ListStore::create( m_columns_color );
     m_treeview_color.set_model( m_liststore_color );
-    m_treeview_color.set_size_request( 480, 280 );
+    m_treeview_color.set_size_request( 480, 250 );
     m_treeview_color.get_selection()->set_mode( Gtk::SELECTION_MULTIPLE );
     m_treeview_color.signal_row_activated().connect( sigc::mem_fun( *this, &FontColorPref::slot_row_activated ) );
     m_scrollwin_color.add( m_treeview_color );
@@ -227,6 +227,10 @@
     m_chk_use_gtkrc_selection.set_active( CONFIG::get_use_select_gtkrc() );
     m_vbox_color.pack_start( m_chk_use_gtkrc_selection, Gtk::PACK_SHRINK );
 
+    m_chk_use_html_color.add_label( "スレビューで HTML タグで指定された文字色を用いる(_H)", true ),
+    m_chk_use_html_color.set_active( CONFIG::get_use_color_html() );
+    m_vbox_color.pack_start( m_chk_use_html_color, Gtk::PACK_SHRINK );
+
     m_bt_reset_all_colors.signal_clicked().connect( sigc::mem_fun( *this, &FontColorPref::slot_reset_all_colors ) );
     m_vbox_color.pack_end( m_bt_reset_all_colors, Gtk::PACK_SHRINK );
 
@@ -256,12 +260,18 @@
 
     CONFIG::set_use_tree_gtkrc( m_chk_use_gtkrc_tree.property_active() );
     CONFIG::set_use_select_gtkrc( m_chk_use_gtkrc_selection.property_active() );
+    std::string completely;
+    const bool use_color = m_chk_use_html_color.property_active();
+    if( use_color != CONFIG::get_use_color_html() ){
+        CONFIG::set_use_color_html( use_color );
+        completely = "completely";
+    }
 
     CORE::core_set_command( "relayout_all_bbslist" );
     CORE::core_set_command( "relayout_all_board" );
 
     CORE::core_set_command( "init_font_all_article" );
-    CORE::core_set_command( "relayout_all_article" );
+    CORE::core_set_command( "relayout_all_article", "", completely );
 
     CORE::core_set_command( "relayout_all_message" );
 }
@@ -511,6 +521,7 @@
     }
     m_chk_use_gtkrc_tree.set_active( CONFIG::CONF_USE_TREE_GTKRC );
     m_chk_use_gtkrc_selection.set_active( CONFIG::CONF_USE_SELECT_GTKRC );
+    m_chk_use_html_color.set_active( CONFIG::CONF_USE_COLOR_HTML );
 
     CONFIG::reset_colors();
 
diff -aurNZ jd-2.8.9-150226/src/fontcolorpref.h jd-2.8.9-a171004/src/fontcolorpref.h
--- jd-2.8.9-150226/src/fontcolorpref.h	2013-02-23 15:23:30.000000000 +0900
+++ jd-2.8.9-a171004/src/fontcolorpref.h	2016-01-16 19:29:55.655586000 +0900
@@ -70,6 +70,7 @@
 
         Gtk::CheckButton m_chk_use_gtkrc_tree;
         Gtk::CheckButton m_chk_use_gtkrc_selection;
+        Gtk::CheckButton m_chk_use_html_color;
 
         Gtk::TreeView m_treeview_color;
         Glib::RefPtr< Gtk::ListStore > m_liststore_color;
diff -aurNZ jd-2.8.9-150226/src/global.h jd-2.8.9-a171004/src/global.h
--- jd-2.8.9-150226/src/global.h	2013-02-23 15:24:45.000000000 +0900
+++ jd-2.8.9-a171004/src/global.h	2017-03-21 04:20:57.158145000 +0900
@@ -11,7 +11,7 @@
     TIMER_TIMEOUT = 50, // msec  内部クロックの周期
     TIMER_TIMEOUT_SMOOTH_SCROLL = 33, // msec  スレビューのスムーススクロール描画用クロック周期
 
-    MAX_RESNUMBER = 11000, // 最大表示可能レス数
+    MAX_RESNUMBER = 65535, // 最大表示可能レス数
 
     MAX_MG_LNG = 5,  // マウスジェスチャの最大ストローク
 
@@ -289,6 +289,7 @@
 
 #define URL_USRCMD "jdpref://usrcmd"
 #define URL_LINKFILTER "jdpref://linkfilter"
+#define URL_REPLACESTR "jdpref://replacestr"
 #define URL_BROWSER "jdpref://browser"
 #define URL_ABOUTCONFIG "jdpref://aboutconfig"
 #define URL_PRIVACY "jdpref://privacy"
diff -aurNZ jd-2.8.9-150226/src/globalabonethreadpref.h jd-2.8.9-a171004/src/globalabonethreadpref.h
--- jd-2.8.9-150226/src/globalabonethreadpref.h	2010-04-11 00:47:00.000000000 +0900
+++ jd-2.8.9-a171004/src/globalabonethreadpref.h	2017-03-22 19:08:39.196004000 +0900
@@ -27,9 +27,13 @@
         Gtk::VBox m_vbox_abone_thread;
         Gtk::Label m_label_abone_thread;
 
-        Gtk::HBox m_hbox_number;
-        Gtk::Label m_label_number;
-        SKELETON::SpinButton m_spin_number;
+        Gtk::HBox m_hbox_min_number;
+        Gtk::Label m_label_min_number;
+        SKELETON::SpinButton m_spin_min_number;
+
+        Gtk::HBox m_hbox_max_number;
+        Gtk::Label m_label_max_number;
+        SKELETON::SpinButton m_spin_max_number;
 
         Gtk::HBox m_hbox_hour;
         Gtk::Label m_label_hour;
@@ -41,7 +45,8 @@
             // 全体あぼーん再設定
 
             // スレ数、時間
-            CONFIG::set_abone_number_thread( m_spin_number.get_value_as_int() );
+            CONFIG::set_abone_min_number_thread( m_spin_min_number.get_value_as_int() );
+            CONFIG::set_abone_max_number_thread( m_spin_max_number.get_value_as_int() );
             CONFIG::set_abone_hour_thread( m_spin_hour.get_value_as_int() );
 
             // word
@@ -68,16 +73,27 @@
             // スレ数、時間
             m_label_abone_thread.set_text( "以下の数字が0の時は未設定になります。\nまたキャッシュにログがあるスレはあぼ〜んされません。\n\n" );
 
-            m_label_number.set_text( "レス以上のスレをあぼ〜ん" );
-            m_spin_number.set_range( 0, 9999 );
-            m_spin_number.set_increments( 1, 1 );
-            m_spin_number.set_value( CONFIG::get_abone_number_thread() );
-            
-            m_hbox_number.set_spacing( 4 );
-            m_hbox_number.pack_start( m_spin_number, Gtk::PACK_SHRINK );
-            m_hbox_number.pack_start( m_label_number, Gtk::PACK_SHRINK );
+            m_label_min_number.set_text( "レス以下のスレをあぼ〜ん" );
+            m_spin_min_number.set_range( 0, 65535 );
+            m_spin_min_number.set_increments( 1, 1 );
+            m_spin_min_number.set_value( CONFIG::get_abone_min_number_thread() );
+
+            m_hbox_min_number.set_spacing( 4 );
+            m_hbox_min_number.pack_start( m_spin_min_number, Gtk::PACK_SHRINK );
+            m_hbox_min_number.pack_start( m_label_min_number, Gtk::PACK_SHRINK );
+
+            set_activate_entry( m_spin_min_number );
+
+            m_label_max_number.set_text( "レス以上のスレをあぼ〜ん" );
+            m_spin_max_number.set_range( 0, 65535 );
+            m_spin_max_number.set_increments( 1, 1 );
+            m_spin_max_number.set_value( CONFIG::get_abone_max_number_thread() );
+
+            m_hbox_max_number.set_spacing( 4 );
+            m_hbox_max_number.pack_start( m_spin_max_number, Gtk::PACK_SHRINK );
+            m_hbox_max_number.pack_start( m_label_max_number, Gtk::PACK_SHRINK );
 
-            set_activate_entry( m_spin_number );
+            set_activate_entry( m_spin_max_number );
 
             m_label_hour.set_text( "時間以上スレ立てから経過したスレをあぼ〜ん" );
             m_spin_hour.set_range( 0, 9999 );
@@ -93,7 +109,8 @@
             m_vbox_abone_thread.set_border_width( 16 );
             m_vbox_abone_thread.set_spacing( 8 );
             m_vbox_abone_thread.pack_start( m_label_abone_thread, Gtk::PACK_SHRINK );
-            m_vbox_abone_thread.pack_start( m_hbox_number, Gtk::PACK_SHRINK );
+            m_vbox_abone_thread.pack_start( m_hbox_min_number, Gtk::PACK_SHRINK );
+            m_vbox_abone_thread.pack_start( m_hbox_max_number, Gtk::PACK_SHRINK );
             m_vbox_abone_thread.pack_start( m_hbox_hour, Gtk::PACK_SHRINK );
 
             // word
diff -aurNZ jd-2.8.9-150226/src/history/historymanager.cpp jd-2.8.9-a171004/src/history/historymanager.cpp
--- jd-2.8.9-150226/src/history/historymanager.cpp	2011-06-21 00:21:47.000000000 +0900
+++ jd-2.8.9-a171004/src/history/historymanager.cpp	2016-02-08 14:31:07.012935000 +0900
@@ -9,6 +9,7 @@
 #include "viewhistoryitem.h"
 
 #include "dbtree/interface.h"
+#include "jdlib/miscutil.h"
 
 #include "xml/document.h"
 #include "xml/tools.h"
@@ -146,7 +147,7 @@
     if( url_history == URL_HISTTHREADVIEW || url_history == URL_HISTCLOSEVIEW ){
 
         info.url = DBTREE::url_dat( url );
-        info.name = DBTREE::article_subject( info.url );
+        info.name = MISC::to_plain( name );
     }
     if( url_history == URL_HISTBOARDVIEW || url_history == URL_HISTCLOSEBOARDVIEW ){
 
diff -aurNZ jd-2.8.9-150226/src/history/historymanager.h jd-2.8.9-a171004/src/history/historymanager.h
--- jd-2.8.9-150226/src/history/historymanager.h	2011-06-21 00:21:47.000000000 +0900
+++ jd-2.8.9-a171004/src/history/historymanager.h	2016-02-15 08:22:57.517904000 +0900
@@ -22,7 +22,7 @@
 {
     class HistoryMenu;
     class ViewHistory;
-    class ViewHistoryItem;
+    struct ViewHistoryItem;
 
     class History_Manager
     {
diff -aurNZ jd-2.8.9-150226/src/history/historymenu.cpp jd-2.8.9-a171004/src/history/historymenu.cpp
--- jd-2.8.9-150226/src/history/historymenu.cpp	2011-01-06 23:58:51.000000000 +0900
+++ jd-2.8.9-a171004/src/history/historymenu.cpp	2017-04-05 21:29:34.116435000 +0900
@@ -21,6 +21,10 @@
 }
 
 
+HistoryMenu::~HistoryMenu()
+{}
+
+
 void HistoryMenu::restore_history()
 {
     m_submenu->restore_history();
diff -aurNZ jd-2.8.9-150226/src/history/historymenu.h jd-2.8.9-a171004/src/history/historymenu.h
--- jd-2.8.9-150226/src/history/historymenu.h	2011-01-06 23:58:51.000000000 +0900
+++ jd-2.8.9-a171004/src/history/historymenu.h	2017-04-05 21:28:59.116435000 +0900
@@ -21,6 +21,7 @@
       public:
 
         HistoryMenu( const std::string& url_history, const std::string& label );
+        virtual ~HistoryMenu();
 
         // 履歴の先頭を復元
         void restore_history();
diff -aurNZ jd-2.8.9-150226/src/history/historysubmenu.cpp jd-2.8.9-a171004/src/history/historysubmenu.cpp
--- jd-2.8.9-150226/src/history/historysubmenu.cpp	2014-03-29 14:23:54.000000000 +0900
+++ jd-2.8.9-a171004/src/history/historysubmenu.cpp	2016-01-09 19:18:44.160868000 +0900
@@ -158,7 +158,7 @@
 
             case TYPE_BOARD:
                 
-                CORE::core_set_command( "open_board" , DBTREE::url_subject( info_list[ i ].url ), tab, mode );
+                CORE::core_set_command( "open_board" , DBTREE::url_boardbase( info_list[ i ].url ), tab, mode );
                 ret = true;
                 break;
 
diff -aurNZ jd-2.8.9-150226/src/httpcode.h jd-2.8.9-a171004/src/httpcode.h
--- jd-2.8.9-150226/src/httpcode.h	2010-09-02 00:33:56.000000000 +0900
+++ jd-2.8.9-a171004/src/httpcode.h	2015-06-18 22:12:03.796523000 +0900
@@ -16,11 +16,15 @@
     HTTP_MOVED_PERM = 301,
     HTTP_REDIRECT = 302,
     HTTP_NOT_MODIFIED = 304,
+    HTTP_AUTH_REQ = 401,
     HTTP_FORBIDDEN = 403,
     HTTP_NOT_FOUND = 404,
     HTTP_TIMEOUT = 408,
     HTTP_RANGE_ERR = 416,
-    HTTP_TEMP_UNAV = 503
+    HTTP_INTERNAL_SERVER_ERR = 500,
+    HTTP_NOT_IMPREMENTED = 501,
+    HTTP_TEMP_UNAV = 503,
+    HTTP_ORIGN_ERR = 520
 };
 
 #endif
diff -aurNZ jd-2.8.9-150226/src/icons/Makefile.am jd-2.8.9-a171004/src/icons/Makefile.am
--- jd-2.8.9-150226/src/icons/Makefile.am	2013-01-06 00:44:26.000000000 +0900
+++ jd-2.8.9-a171004/src/icons/Makefile.am	2017-04-11 20:38:55.185703000 +0900
@@ -10,7 +10,7 @@
 	play.h hist.h hist_board.h hist_close.h hist_closeboard.h hist_closeimg.h info.h
 
 noinst_HEADERS = \
-	iconmanager.h $(icon_headers)
+	iconfiles.h iconid.h iconmanager.h $(icon_headers)
 
 AM_CXXFLAGS = @GTKMM_CFLAGS@
 AM_CPPFLAGS = -I$(top_srcdir)/src
diff -aurNZ jd-2.8.9-150226/src/icons/iconfiles.h jd-2.8.9-a171004/src/icons/iconfiles.h
--- jd-2.8.9-150226/src/icons/iconfiles.h	2011-09-05 18:14:19.000000000 +0900
+++ jd-2.8.9-a171004/src/icons/iconfiles.h	2016-02-15 08:25:38.253902000 +0900
@@ -3,7 +3,7 @@
 // アイコンテーマのファイル
 
 #ifndef _ICONFILES_H
-#define _ICONFILSE_H
+#define _ICONFILES_H
 
 enum
 {
diff -aurNZ jd-2.8.9-150226/src/image/imageview.cpp jd-2.8.9-a171004/src/image/imageview.cpp
--- jd-2.8.9-150226/src/image/imageview.cpp	2011-04-06 01:51:47.000000000 +0900
+++ jd-2.8.9-a171004/src/image/imageview.cpp	2016-03-17 21:39:24.972067000 +0900
@@ -372,7 +372,12 @@
             m_length_prev = get_img()->current_length();
 
             char tmpstr[ 256 ];
-            snprintf( tmpstr, 256, "%zd k / %zd k", m_length_prev/1024, get_img()->total_length()/1024 );
+#ifdef _WIN32
+            snprintf( tmpstr, sizeof( tmpstr ), "%u / %u KiB", m_length_prev/1024, get_img()->total_length()/1024 );
+#else
+            snprintf( tmpstr, sizeof( tmpstr ), "%zu / %zu KiB", m_length_prev/1024, get_img()->total_length()/1024 );
+#endif
+            tmpstr[ sizeof( tmpstr ) - 1 ] = '\0';
             set_status_local( tmpstr );
             add_tab_number();
 
diff -aurNZ jd-2.8.9-150226/src/image/imageviewbase.h jd-2.8.9-a171004/src/image/imageviewbase.h
--- jd-2.8.9-150226/src/image/imageviewbase.h	2011-04-03 23:10:59.000000000 +0900
+++ jd-2.8.9-a171004/src/image/imageviewbase.h	2016-03-15 21:32:24.066722000 +0900
@@ -64,7 +64,7 @@
         const bool is_wait() const{ return m_wait; }
         void set_wait( const bool wait ){ m_wait = wait; }
 
-        const bool is_loading() const{ return m_loading; }
+        const bool is_loading() const { return m_loading; }
         void set_loading( const bool loading ){ m_loading = loading; }
 
         Gtk::EventBox& get_event(){ return  m_event; }
diff -aurNZ jd-2.8.9-150226/src/image/imageviewpopup.cpp jd-2.8.9-a171004/src/image/imageviewpopup.cpp
--- jd-2.8.9-150226/src/image/imageviewpopup.cpp	2010-02-08 23:48:14.000000000 +0900
+++ jd-2.8.9-a171004/src/image/imageviewpopup.cpp	2016-03-17 21:40:01.176066000 +0900
@@ -304,7 +304,12 @@
         m_length_prev = get_img()->current_length();
 
         char tmpstr[ 256 ];
-        snprintf( tmpstr, 256, "%zd k / %zd k", m_length_prev/1024, get_img()->total_length()/1024 );
+#if _WIN32
+        snprintf( tmpstr, sizeof( tmpstr ), "%u / %u KiB", m_length_prev/1024, get_img()->total_length()/1024 );
+#else
+        snprintf( tmpstr, sizeof( tmpstr ), "%zu / %zu KiB", m_length_prev/1024, get_img()->total_length()/1024 );
+#endif
+        tmpstr[ sizeof( tmpstr ) - 1 ] = '\0';
         m_label->set_text( tmpstr );
     }
 }
diff -aurNZ jd-2.8.9-150226/src/image/preference.cpp jd-2.8.9-a171004/src/image/preference.cpp
--- jd-2.8.9-150226/src/image/preference.cpp	2009-05-23 13:04:01.000000000 +0900
+++ jd-2.8.9-a171004/src/image/preference.cpp	2016-02-08 12:57:13.993022000 +0900
@@ -33,7 +33,7 @@
     const std::string daturl = DBTREE::url_dat( refurl, num_from, num_to, num_str );
     const std::string readcgi = DBTREE::url_readcgi( daturl, num_from, 0 );
 
-    m_label_ref.set_text( DBTREE::article_subject( daturl ) );
+    m_label_ref.set_text( MISC::to_plain( DBTREE::article_modified_subject( daturl ) ) );
     m_label_url_ref.set_text( readcgi );
 
     m_open_ref.signal_clicked().connect( sigc::mem_fun(*this, &Preferences::slot_open_ref ) );
diff -aurNZ jd-2.8.9-150226/src/jdlib/Makefile.am jd-2.8.9-a171004/src/jdlib/Makefile.am
--- jd-2.8.9-150226/src/jdlib/Makefile.am	2013-01-06 00:44:26.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/Makefile.am	2016-04-12 22:56:38.370244000 +0900
@@ -13,7 +13,7 @@
 	jdiconv.cpp \
 	loader.cpp \
 	imgloader.cpp \
-	ssl.cpp \
+	jdsocket.cpp \
 	loaderdata.cpp \
 	confloader.cpp \
 	jdregex.cpp \
@@ -37,7 +37,7 @@
 	jdiconv.h \
 	loader.h \
 	imgloader.h \
-	ssl.h \
+	jdsocket.h \
 	loaderdata.h \
 	confloader.h \
 	jdregex.h \
@@ -47,5 +47,5 @@
 	timeout.h \
 	hkana.h
 
-AM_CXXFLAGS = @GTKMM_CFLAGS@ @GNUTLS_CFLAGS@ @OPENSSL_CFLAGS@ @X11_CFLAGS@
+AM_CXXFLAGS = @GTKMM_CFLAGS@ @GNUTLS_CFLAGS@ @OPENSSL_CFLAGS@ @NSS_CFLAGS@ @X11_CFLAGS@
 AM_CPPFLAGS = -I$(top_srcdir)/src
diff -aurNZ jd-2.8.9-150226/src/jdlib/confloader.cpp jd-2.8.9-a171004/src/jdlib/confloader.cpp
--- jd-2.8.9-150226/src/jdlib/confloader.cpp	2009-06-16 00:34:09.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/confloader.cpp	2016-02-02 21:28:48.560498000 +0900
@@ -38,14 +38,12 @@
         std::list < std::string >::iterator it = lines.begin();
         for( ; it != lines.end(); ++it ){
 
-            std::string line = MISC::remove_space( ( *it ) );
-
-            size_t i = line.find( "=" );
+            size_t i = it->find_first_of( '=' );
             if( i != std::string::npos ){
 
                 ConfData data;
-                data.name = MISC::remove_space( line.substr( 0, i ) );
-                data.value = MISC::remove_space( line.substr( i +1 ) );
+                data.name = MISC::remove_space( it->substr( 0, i ) );
+                data.value = MISC::remove_space( it->substr( i + 1 ) );
                 m_data.push_back( data );
 #ifdef _DEBUG
                 std::cout << data.name << " = " << data.value << std::endl;
diff -aurNZ jd-2.8.9-150226/src/jdlib/confloader.h jd-2.8.9-a171004/src/jdlib/confloader.h
--- jd-2.8.9-150226/src/jdlib/confloader.h	2009-06-02 23:29:52.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/confloader.h	2016-02-08 14:43:48.564924000 +0900
@@ -10,8 +10,6 @@
 #include <string>
 #include <list>
 
-#include "config/defaultconf.h"
-
 namespace JDLIB
 {
     struct ConfData
diff -aurNZ jd-2.8.9-150226/src/jdlib/constptr.h jd-2.8.9-a171004/src/jdlib/constptr.h
--- jd-2.8.9-150226/src/jdlib/constptr.h	2006-12-14 22:47:07.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/constptr.h	2016-02-13 15:48:19.850242000 +0900
@@ -17,6 +17,7 @@
     public:
 
         T* operator -> (){ return m_p; }
+        const T* operator -> () const { return m_p; }
         bool operator == ( const T *p ) const { return( m_p == p ); }
         bool operator != ( const T *p ) const { return( m_p != p ); }
         bool operator ! () const { return ( m_p == NULL ); }
diff -aurNZ jd-2.8.9-150226/src/jdlib/hash_set.cpp jd-2.8.9-a171004/src/jdlib/hash_set.cpp
--- jd-2.8.9-150226/src/jdlib/hash_set.cpp	2010-04-15 00:38:01.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/hash_set.cpp	2017-03-29 01:21:56.307713000 +0900
@@ -4,11 +4,10 @@
 #include "jddebug.h"
 
 #include "hash_set.h"
+#include "misctrip.h"
 
 #include "dbtree/interface.h"
 
-#include <cstdlib>
-
 
 using namespace JDLIB;
 
@@ -51,7 +50,7 @@
 }
 
 
-const bool simple_hash_set::find_if( const std::string& item )
+bool simple_hash_set::find_if( const std::string& item ) const
 {
     if( ! m_hash.size() ) return false;
 
@@ -60,6 +59,17 @@
 }
 
 
+int simple_hash_set::get_key( const std::string& item ) const
+{
+    int key = MISC::fnv_hash( item.c_str(), item.length() ) % size();
+
+#ifdef _DEBUG
+    std::cout << "simple_hash_set::get_key item = " << item << " key = " << key << std::endl;
+#endif
+
+    return key;
+}
+
 
 //////////////////////////////
 
@@ -77,15 +87,9 @@
 {}
 
 
-const int hash_set_thread::get_key( const std::string& url )
+int hash_set_thread::get_key( const std::string& url ) const
 {
-
     const int lng = DBTREE::url_datbase( url ).length();
-    const int key = atoi(  url.substr( lng < (int) url.length() ? lng : 0  ).c_str() ) % size();
-
-#ifdef _DEBUG
-    std::cout << "hash_set_thread::get_key url = " << url << " key = " << key << std::endl;
-#endif
-
-    return key ;
+    const int pos = lng < (int) url.length() ? lng : 0;
+    return simple_hash_set::get_key( url.substr( pos ) );
 }
diff -aurNZ jd-2.8.9-150226/src/jdlib/hash_set.h jd-2.8.9-a171004/src/jdlib/hash_set.h
--- jd-2.8.9-150226/src/jdlib/hash_set.h	2010-03-22 22:24:20.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/hash_set.h	2017-03-25 17:15:24.912135000 +0900
@@ -23,16 +23,16 @@
         simple_hash_set( const int hash_size );
         virtual ~simple_hash_set();
 
-        const int size() const { return m_hash_size; }
+        int size() const { return m_hash_size; }
 
         void clear();
         void insert( const std::string& item );
         void erase( const std::string& item );
-        const bool find_if( const std::string& item );
+        bool find_if( const std::string& item ) const;
 
-      private:
+      protected:
 
-        virtual const int get_key( const std::string& item ) = 0;
+        virtual int get_key( const std::string& item ) const;
     };
 
 
@@ -48,7 +48,7 @@
 
       private:
 
-        virtual const int get_key( const std::string& url );
+        virtual int get_key( const std::string& url ) const;
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/heap.cpp jd-2.8.9-a171004/src/jdlib/heap.cpp
--- jd-2.8.9-150226/src/jdlib/heap.cpp	2008-05-17 00:34:07.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/heap.cpp	2015-06-07 02:29:37.808180000 +0900
@@ -59,13 +59,18 @@
         m_used = 0;
 
 #ifdef _DEBUG
-        std::cout << "HEAP::heap_alloc malloc max = " << m_max <<  " total = " << m_total_size + n + 4 << std::endl;
+        std::cout << "HEAP::heap_alloc malloc max = " << m_max <<  " total = " << m_total_size + n << std::endl;
 #endif
     }
 
     unsigned char* heap = m_heap_list.back() + m_used;
-    m_used += n + 4;
-    m_total_size += n + 4;
+#ifdef __i386__
+    if( n & 3 ) n = ( n + 4 ) & ~ 3;
+#else // __x86_64__ or etc
+    if( n & 7 ) n = ( n + 8 ) & ~ 7;
+#endif
+    m_used += n;
+    m_total_size += n;
     
     return heap;
 }
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdiconv.cpp jd-2.8.9-a171004/src/jdlib/jdiconv.cpp
--- jd-2.8.9-150226/src/jdlib/jdiconv.cpp	2015-01-31 00:22:51.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdiconv.cpp	2016-01-13 18:04:29.587638000 +0900
@@ -9,9 +9,12 @@
 #endif
 
 #include "jdiconv.h"
+#include "misccharcode.h"
 #include "miscmsg.h"
 #include "miscutil.h"
 
+#include "config/globalconf.h"
+
 #include <errno.h>
 #include <cstring>
 #include <cstdio>
@@ -19,42 +22,46 @@
 
 using namespace JDLIB;
 
-Iconv::Iconv( const std::string& coding_from, const std::string& coding_to )
-    : m_buf_in( 0 ), m_buf_out( 0 ), m_coding_from( coding_from )
+Iconv::Iconv( const CharCode from, const CharCode to )
+    : m_cd( (iconv_t)-1 )
+    , m_buf_size( BUF_SIZE_ICONV_OUT )
+    , m_coding_from( from )
+    , m_coding_to( to )
 {
 #ifdef _DEBUG
-    std::cout << "Iconv::Iconv coding = " << m_coding_from << " to " << coding_to << std::endl;
+    std::cout << "Iconv::Iconv coding = " << m_coding_from << " to " << m_coding_to << std::endl;
 #endif
     
-    m_buf_in = ( char* )malloc( BUF_SIZE_ICONV_IN );
-    m_buf_out = ( char* )malloc( BUF_SIZE_ICONV_OUT );
+    m_buf = ( char* )malloc( m_buf_size );
+    const char* from_str = MISC::charcode_to_cstr( ( from == CHARCODE_UNKNOWN ) ? to : from );
+    const char* to_str = MISC::charcode_to_cstr( ( to == CHARCODE_UNKNOWN ) ? from : to );
     
-    m_cd = iconv_open( coding_to.c_str(), m_coding_from.c_str() ); 
+    m_cd = iconv_open( to_str, from_str );
 
     // MS932で失敗したらCP932で試してみる
     if( m_cd == ( iconv_t ) -1 ){
-        if( coding_to == "MS932" ) m_cd = iconv_open( "CP932", m_coding_from.c_str() );
-        else if( coding_from == "MS932" ) m_cd = iconv_open( coding_to.c_str(), "CP932" ); 
+        if( to == CHARCODE_SJIS ) m_cd = iconv_open( "CP932", from_str );
+        else if( from == CHARCODE_SJIS ) m_cd = iconv_open( to_str, "CP932" );
     }
 
     // "EUCJP-*"で失敗したら"EUCJP"で試してみる
     if( m_cd == ( iconv_t ) - 1 && ( errno & EINVAL ) != 0 )
     {
-        if( coding_to.find( "EUCJP-", 0 ) == 0 )
-		{
-            m_cd = iconv_open( "EUCJP//TRANSLIT", coding_from.c_str() );
-        }
-        else if( coding_from.find( "EUCJP-", 0 ) == 0 )
-		{
-            const std::string coding_to_translit = coding_to + "//TRANSLIT";
+        if( to == CHARCODE_EUCJP ) m_cd = iconv_open( "EUCJP//TRANSLIT", from_str );
+        else if( from == CHARCODE_EUCJP )
+        {
+            const std::string coding_to_translit = std::string( to_str ) + "//TRANSLIT";
             m_cd = iconv_open( coding_to_translit.c_str(), "EUCJP" );
         }
     }
 
     if( m_cd == ( iconv_t ) -1 ){
-        MISC::ERRMSG( "can't open iconv coding = " + m_coding_from + " to " + coding_to );
+        std::string msg = "can't open iconv coding = ";
+        msg += MISC::charcode_to_cstr( from );
+        msg += " to ";
+        msg += MISC::charcode_to_cstr( to );
+        MISC::ERRMSG( msg );
     }
-    m_byte_left_in = 0;
 }
 
 Iconv::~Iconv()
@@ -63,51 +70,45 @@
     std::cout << "Iconv::~Iconv\n";
 #endif    
     
-    if( m_buf_in ) free( m_buf_in );
-    if( m_buf_out ) free( m_buf_out );
+    if( m_buf ) free( m_buf );
     if( m_cd != ( iconv_t ) -1 ) iconv_close( m_cd );
 }
 
 
-const char* Iconv::convert( char* str_in, int size_in, int& size_out )
+const char* Iconv::convert( const char* str_in, int size_in, int& size_out )
 {
 #ifdef _DEBUG
-    std::cout << "Iconv::convert size_in = " << size_in 
-              <<" left = " << m_byte_left_in << std::endl;
+    std::cout << "Iconv::convert size_in = " << size_in << std::endl;
 #endif
 
-    assert( m_byte_left_in + size_in < BUF_SIZE_ICONV_IN );
     if( m_cd == ( iconv_t ) -1 ) return NULL;
     
-    size_t byte_left_out = BUF_SIZE_ICONV_OUT;
-    char* buf_out = m_buf_out;
+    const char* buf_in_tmp = str_in;
+    const char* buf_in_end = str_in + size_in;
 
-    // 前回の残りをコピー
-    if( m_byte_left_in ){
-        memcpy( m_buf_in, m_buf_in_tmp, m_byte_left_in );
-        m_buf_in_tmp = m_buf_in;
-        memcpy( m_buf_in + m_byte_left_in , str_in, size_in );    
-    }
-    else m_buf_in_tmp = str_in;
+    char* buf_out_tmp = m_buf;
+    char* buf_out_end = m_buf + m_buf_size;
 
-    m_byte_left_in += size_in;
+    const char* pre_check = NULL; // 前回チェックしたUTF-8の先頭
 
     // iconv 実行
     do{
 
+        size_t byte_left_in = buf_in_end - buf_in_tmp;
+        size_t byte_left_out = buf_out_end - buf_out_tmp;
+
 #ifdef _DEBUG
-        std::cout << "m_byte_left_in = " << m_byte_left_in << std::endl;
+        std::cout << "byte_left_in = " << byte_left_in << std::endl;
         std::cout << "byte_left_out = " << byte_left_out << std::endl;
 #endif
     
-        const int ret = iconv( m_cd, ( ICONV_CONST char**)&m_buf_in_tmp, &m_byte_left_in, &buf_out, &byte_left_out );
+        const int ret = iconv( m_cd, (ICONV_CONST char**)&buf_in_tmp, &byte_left_in, &buf_out_tmp, &byte_left_out );
 
 #ifdef _DEBUG
         std::cout << "--> ret = " << ret << std::endl;
-        std::cout << "m_byte_left_in = " << m_byte_left_in << std::endl;
+        std::cout << "byte_left_in = " << byte_left_in << std::endl;
         std::cout << "byte_left_out = " << byte_left_out << std::endl;
 #endif
-        assert( byte_left_out >= 0 );
 
         // エラー
         if( ret == -1 ){
@@ -117,66 +118,91 @@
 #ifdef _DEBUG_ICONV
                 char str_tmp[256];
 #endif
-                const unsigned char code0 = *m_buf_in_tmp;
-                const unsigned char code1 = *(m_buf_in_tmp+1);
-                const unsigned char code2 = *(m_buf_in_tmp+2);
-
-                if( m_coding_from == "MS932" )
-                {
-
-                    // 空白(0xa0)
-                    if( code0 == 0xa0 ){
-                        *m_buf_in_tmp = 0x20;
-                        continue;
-                    }
-
-                    // <>の誤判別 ( 開発スレ 489 を参照 )
-                    if( code1 == 0x3c && code2 == 0x3e ){
-                        *m_buf_in_tmp = '?';
+                const unsigned char code0 = *buf_in_tmp;
 #ifdef _DEBUG_ICONV
-                        snprintf( str_tmp, 256, "iconv 0x%x%x> -> ?<>", code0, code1 );
-                        MISC::MSG( str_tmp );
+                const unsigned char code1 = *( buf_in_tmp + 1 );
+                const unsigned char code2 = *( buf_in_tmp + 2 );
 #endif
-                        continue;
-                    }
 
-                    // マッピング失敗
-                    // □(0x81a0)を表示する
-                    if( ( code0 >= 0x81 && code0 <=0x9F )
-                        || ( code0 >= 0xe0 && code0 <=0xef ) ){
+                // MS932からUTF-8へマッピング失敗
+                if( m_coding_from == CHARCODE_SJIS && m_coding_to == CHARCODE_UTF8
+                        && CONFIG::get_broken_sjis_be_utf8() ){
+
+                    // UTF-8へ変換する場合UTF-8の混在を許容する
+                    const char* bpos = buf_in_tmp;
+                    const char* epos = buf_in_tmp;
+
+                    // マルチバイトUTF-8文字列の先頭と終端を調べる
+                    while( bpos > str_in && *( bpos - 1 ) < 0 ) --bpos;
+                    while( epos < ( buf_in_tmp + byte_left_in ) && *epos < 0 ) ++epos;
+
+                    size_t lng = epos - bpos;
 
-                        *m_buf_in_tmp = 0x81;
-                        *(m_buf_in_tmp+1) = 0xa0;
+                    // 1byteは不正、また毎回同じ文字列を判定しない
+                    if( lng > 1 && bpos != pre_check ){
 
+                        // 一文字ずつUTF-8の文字か確認
+                        size_t byte = 0;
+                        pre_check = bpos;
+
+                        if( MISC::is_utf8( bpos, lng, byte ) ){
 #ifdef _DEBUG_ICONV
-                        snprintf( str_tmp, 256, "iconv 0x%x%x -> □ (0x81a0) ", code0, code1 );
-                        MISC::MSG( str_tmp );
-#endif
-                        continue;
+                            std::string utf8( bpos, lng );
+                            snprintf( str_tmp, 256, "iconv to be utf-8: %s", utf8.c_str() );
+                            MISC::MSG( str_tmp );
+#endif
+
+                            const char* const span_bgn = "<span class=\"BROKEN_SJIS\">";
+                            const char* const span_end = "</span>";
+                            const size_t lng_span_bgn = strlen( span_bgn );
+                            const size_t lng_span_end = strlen( span_end );
+
+                            while( buf_out_tmp > m_buf && *( buf_out_tmp - 1 ) < 0 ) --buf_out_tmp;
+                            if( ( buf_out_tmp + lng + lng_span_bgn + lng_span_end ) >= buf_out_end ){
+                                size_t used = buf_out_tmp - m_buf;
+                                if( ! grow() ) break;
+                                buf_out_tmp = m_buf + used;
+                                buf_out_end = m_buf + m_buf_size;
+                            }
+                            memcpy( buf_out_tmp, span_bgn, lng_span_bgn );
+                            buf_out_tmp += lng_span_bgn;
+                            memcpy( buf_out_tmp, bpos, lng );
+                            buf_out_tmp += lng;
+                            memcpy( buf_out_tmp, span_end, lng_span_end );
+                            buf_out_tmp += lng_span_end;
+                            buf_in_tmp = bpos + lng;
+
+                            continue;
+                        }
                     }
                 }
 
                 // unicode 文字からの変換失敗
                 // 数値文字参照(&#????;)形式にする
-                if( m_coding_from == "UTF-8" ){
+                if( m_coding_from == CHARCODE_UTF8 ){
 
                     int byte;
-                    const int ucs2 = MISC::utf8toucs2( m_buf_in_tmp, byte );
-                    if( byte != 1 ){
+                    const int code = MISC::utf8tocp( buf_in_tmp, byte );
+                    if( byte > 1 ){
 
-                        const std::string ucs2_str = MISC::itostr( ucs2 );
+                        const std::string ucs_str = MISC::itostr( code );
 #ifdef _DEBUG
-                        std::cout << "ucs2 = " << ucs2_str << " byte = " << byte << std::endl;
+                        std::cout << "ucs = " << ucs_str << " byte = " << byte << std::endl;
 #endif
-                        m_buf_in_tmp += byte;
-                        m_byte_left_in -= byte;
-
-                        *(buf_out++) = '&';
-                        *(buf_out++) = '#';
-                        memcpy( buf_out, ucs2_str.c_str(), ucs2_str.size() ); buf_out += ucs2_str.size();
-                        *(buf_out++) = ';';
+                        buf_in_tmp += byte;
 
-                        byte_left_out -= ucs2_str.size() + 3;
+                        if( ( buf_out_tmp + ucs_str.length() + 3 ) >= buf_out_end ){
+                            size_t used = buf_out_tmp - m_buf;
+                            if( ! grow() ) break;
+                            buf_out_tmp = m_buf + used;
+                            buf_out_end = m_buf + m_buf_size;
+                        }
+
+                        *(buf_out_tmp++) = '&';
+                        *(buf_out_tmp++) = '#';
+                        ucs_str.copy( buf_out_tmp, ucs_str.length() );
+                        buf_out_tmp += ucs_str.length();
+                        *(buf_out_tmp++) = ';';
 
                         continue;
                     }
@@ -185,12 +211,31 @@
                 // 時々空白(0x20)で EILSEQ が出るときがあるのでもう一度トライする
                 if( code0 == 0x20 ) continue;
 
-                //その他、1文字を空白にして続行
 #ifdef _DEBUG_ICONV
-                snprintf( str_tmp, 256, "iconv EILSEQ left = %zd code = %x %x %x", m_byte_left_in, code0, code1, code2 );
+                snprintf( str_tmp, 256, "iconv EILSEQ left = %zd code = %x %x %x", byte_left_in, code0, code1, code2 );
                 MISC::ERRMSG( str_tmp );
 #endif
-                *m_buf_in_tmp = 0x20;
+
+                // BOFの確認
+                if( ( buf_out_end - buf_out_tmp ) <= 3 ){
+                    size_t used = buf_out_tmp - m_buf;
+                    if( ! grow() ) break;
+                    buf_out_tmp = m_buf + used;
+                    buf_out_end = m_buf + m_buf_size;
+                }
+
+                // UTF-8へ変換する場合はREPLACEMENT CHARACTERに置き換える
+                if( m_coding_to == CHARCODE_UTF8 ){
+                    ++buf_in_tmp;
+                    *(buf_out_tmp++) = 0xef;
+                    *(buf_out_tmp++) = 0xbf;
+                    *(buf_out_tmp++) = 0xbd;
+                    continue;
+                }
+
+                //その他、1文字を?にして続行
+                ++buf_in_tmp;
+                *(buf_out_tmp++) = '?';
             }
 
             else if( errno == EINVAL ){
@@ -204,14 +249,32 @@
 #ifdef _DEBUG_ICONV
                 MISC::ERRMSG( "iconv E2BIG\n" );
 #endif
-                break;
+                size_t used = buf_out_tmp - m_buf;
+                if( ! grow() ) break;
+                buf_out_tmp = m_buf + used;
+                buf_out_end = m_buf + m_buf_size;
+                continue;
             }
         }
     
-    } while( m_byte_left_in > 0 );
+    } while( buf_in_tmp < buf_in_end );
 
-    size_out = BUF_SIZE_ICONV_OUT - byte_left_out;
-    m_buf_out[ size_out ] = '\0';
+    size_out = buf_out_tmp - m_buf;
+    *buf_out_tmp = '\0';
     
-    return m_buf_out;
+#ifdef _DEBUG
+    std::cout << "Iconv::convert size_out = " << size_out << std::endl;
+#endif
+    return m_buf;
+}
+
+
+
+bool Iconv::grow()
+{
+    m_buf_size += BUF_SIZE_ICONV_OUT;
+    char *tmp = ( char* )realloc( m_buf, m_buf_size );
+    if( ! tmp ) return false;
+    m_buf = tmp;
+    return true;
 }
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdiconv.h jd-2.8.9-a171004/src/jdlib/jdiconv.h
--- jd-2.8.9-150226/src/jdlib/jdiconv.h	2007-12-01 21:14:20.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdiconv.h	2015-12-29 19:57:24.249996000 +0900
@@ -3,15 +3,14 @@
 #ifndef _JDICONV_H
 #define _JDICONV_H
 
+#include "charcode.h"
+
 #include <iconv.h>
 #include <string>
 
-// iconv の内部で確保するバッファサイズ(バイト)
-//  BUF_SIZE_ICONV_IN を超える入力は扱えないので注意
 enum
 {
-    BUF_SIZE_ICONV_IN = 1024 * 1024,
-    BUF_SIZE_ICONV_OUT = BUF_SIZE_ICONV_IN /2 * 3
+    BUF_SIZE_ICONV_OUT = 512 * 1024,
 };
 
 namespace JDLIB
@@ -20,20 +19,21 @@
     {
         iconv_t m_cd;
 
-        size_t m_byte_left_in;
-        char* m_buf_in;
-        char* m_buf_in_tmp;
-
-        char* m_buf_out;
+        char* m_buf;
+        size_t m_buf_size;
 
-        std::string m_coding_from;
+        CharCode m_coding_from;
+        CharCode m_coding_to;
 
     public:
         
-        Iconv( const std::string& coding_from, const std::string& coding_to );
+        Iconv( const CharCode from, const CharCode to );
         ~Iconv();
 
-        const char* convert( char* str_in, int size_in, int& size_out );
+        const char* convert( const char* str_in, int size_in, int& size_out );
+
+    private:
+        bool grow();
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdmigemo.cpp jd-2.8.9-a171004/src/jdlib/jdmigemo.cpp
--- jd-2.8.9-150226/src/jdlib/jdmigemo.cpp	2008-02-08 23:41:57.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdmigemo.cpp	2017-04-27 21:24:15.555237000 +0900
@@ -24,20 +24,20 @@
 migemo *migemo_object;
 
 
-int jd_migemo_regcomp(regex_t *preg,const char *regex,int cflags)
+char* jd_migemo_regcreate(const char *regex,int cflags)
 {
     migemo *m;
-    int retval,i;
+    int i;
     size_t len;
     unsigned char *p,*utmp;
     char *ctmp;
     m=migemo_object;
     if(!m){
-        return -1;
+        return NULL;
     }
     //migemo_load(m,MIGEMO_DICTID_MIGEMO,JD_MIGEMO_DICTNAME);
     if(!migemo_is_enable(m)){
-        return -1;
+        return NULL;
     }
     utmp=(unsigned char *)calloc(sizeof(unsigned char),strlen(regex)+1);
     memcpy(utmp,regex,strlen(regex));
@@ -49,21 +49,16 @@
     }
     p=migemo_query(m,utmp);
     free(utmp);
-    for(len=0;p[len]!='\0';len++);
-    ctmp=(char *)calloc(sizeof(char),len+1);
+    len=strlen((char*)p)+1;
+    ctmp=(char *)malloc(len);
     memcpy(ctmp,p,len);
 
 #ifdef _DEBUG
     std::cout << "migemo comp:" << ctmp << std::endl;
 #endif
 
-    retval=regcomp(preg,ctmp,cflags);
-    free(ctmp);
-    if(retval!=0){
-        regfree(preg);
-    }
     migemo_release(m,p);
-    return retval;
+    return ctmp;
 }
 
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdmigemo.h jd-2.8.9-a171004/src/jdlib/jdmigemo.h
--- jd-2.8.9-150226/src/jdlib/jdmigemo.h	2008-12-19 23:06:42.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdmigemo.h	2016-03-26 20:06:20.955782000 +0900
@@ -12,13 +12,12 @@
 #ifdef HAVE_MIGEMO_H
 
 #include <migemo.h>
-#include "jdregex.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-    int jd_migemo_regcomp(regex_t *preg,const char *regex,int cflags);
+    char* jd_migemo_regcreate(const char *regex,int cflags);
     int jd_migemo_init(const char *filename);
     int jd_migemo_close(void);
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdregex.cpp jd-2.8.9-a171004/src/jdlib/jdregex.cpp
--- jd-2.8.9-150226/src/jdlib/jdregex.cpp	2013-04-04 22:26:24.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdregex.cpp	2016-06-08 21:40:45.071338000 +0900
@@ -20,52 +20,67 @@
 
 using namespace JDLIB;
 
-Regex::Regex()
-    : m_compiled(false),
-      m_target_asc( NULL ),
-      m_table_pos( NULL )
+
+RegexPattern::RegexPattern( const std::string& reg, const bool icase, const bool newline, const bool usemigemo, const bool wchar, const bool norm )
+    : m_compiled( false )
+    , m_error( 0 )
 {
-    m_results.clear();
-    m_pos.clear();
+    set( reg, icase, newline, usemigemo, wchar, norm );
 }
 
 
-Regex::~Regex()
+RegexPattern::~RegexPattern()
 {
-    dispose();
-
-    if( m_target_asc ) free( m_target_asc );
-    if( m_table_pos ) free( m_table_pos );
+    clear();
 }
 
-
-void Regex::dispose()
+void RegexPattern::clear()
 {
-    if ( m_compiled ) {
-        regfree( &m_reg );
-        m_compiled = false;
-    }
-}
+    if( m_compiled )
+#if USE_REGEX_COMPAT
+        regfree( &m_regex );
+#else
+        g_regex_unref( m_regex );
+#endif
+    m_compiled = false;
 
+#if USE_REGEX_COMPAT
+    m_error = 0;
+#else
+    g_clear_error( &m_error );
+#endif
+}
 
 // icase : 大文字小文字区別しない
 // newline :  . に改行をマッチさせない
 // usemigemo : migemo使用 (コンパイルオプションで指定する必要あり)
 // wchar : 全角半角の区別をしない
-const bool Regex::compile( const std::string reg, const bool icase, const bool newline, const bool use_migemo, const bool wchar )
+bool RegexPattern::set( const std::string& reg, const bool icase, const bool newline, const bool usemigemo, const bool wchar, const bool norm )
 {
 #ifdef _DEBUG
     if( wchar ){
-        std::cout << "Regex::compile\n";
-        std::cout << reg << std::endl;
+        std::cout << "RegexPattern::set " << reg << std::endl;
     }
 #endif
 
-    dispose();
+    if( m_compiled ){
+#if USE_REGEX_COMPAT
+        regfree( &m_regex );
+#else
+        g_regex_unref( m_regex );
+#endif
+        m_compiled = false;
+    }
     
+#if USE_REGEX_COMPAT
+    m_error = 0;
+#else
+    g_clear_error( &m_error );
+#endif
     if( reg.empty() ) return false;
     
-#ifdef USE_PCRE
+#if USE_REGEX_COMPAT
+#ifdef HAVE_PCREPOSIX_H
     int cflags = REG_UTF8;
     if( ! newline ) cflags |= REG_DOTALL; // . を改行にマッチさせる
 #else
@@ -73,83 +88,181 @@
 #endif
     if( newline ) cflags |= REG_NEWLINE;
     if( icase ) cflags |= REG_ICASE;
+#else
+    int cflags = G_REGEX_OPTIMIZE;
+    if( newline ) cflags |= G_REGEX_MULTILINE;
+    else cflags |= G_REGEX_DOTALL; // . を改行にマッチさせる
+    if( icase ) cflags |= G_REGEX_CASELESS;
+#endif //USE_REGEX_COMPAT
 
     m_newline = newline;
     m_wchar = wchar;
+    m_norm = norm;
 
     const char* asc_reg = reg.c_str();
+    char* target_asc = NULL;
 
-    // 全角英数字 → 半角英数字、半角カナ → 全角カナ
-    if( m_wchar && MISC::has_widechar( asc_reg ) ){
+    // Unicode正規化
+    if( m_norm ){
+        target_asc = ( char* )malloc( MAX_TARGET_SIZE );
+        MISC::norm( asc_reg, target_asc, NULL, MAX_TARGET_SIZE );
+        asc_reg = target_asc;
 
-        if( ! m_target_asc ) m_target_asc = ( char* )malloc( MAX_TARGET_SIZE );
-        if( ! m_table_pos ) m_table_pos = ( int* )malloc( MAX_TARGET_SIZE );
+#ifdef _DEBUG
+        std::cout << target_asc << std::endl;
+#endif
+    }
+
+    // 全角英数字 → 半角英数字、半角カナ → 全角カナ
+    else if( m_wchar && MISC::has_widechar( asc_reg ) ){
 
-        MISC::asc( asc_reg, m_target_asc, m_table_pos, MAX_TARGET_SIZE );
-        asc_reg = m_target_asc;
+        target_asc = ( char* )malloc( MAX_TARGET_SIZE );
+        MISC::asc( asc_reg, target_asc, NULL, MAX_TARGET_SIZE );
+        asc_reg = target_asc;
 
 #ifdef _DEBUG
-        std::cout << m_target_asc << std::endl;
+        std::cout << target_asc << std::endl;
 #endif
     }
 
 #ifdef HAVE_MIGEMO_H
 
-    if( use_migemo ){
-
-        if( jd_migemo_regcomp( &m_reg, asc_reg, cflags ) != 0 ){
-
-            if( regcomp( &m_reg, asc_reg, cflags ) != 0 ){
-                regfree( &m_reg );
-                return false;
-            }
+    if( usemigemo ){
+        char *mgm_reg = jd_migemo_regcreate( asc_reg, cflags );
+        if( mgm_reg != NULL ){
+#if USE_REGEX_COMPAT
+            m_error = regcomp( &m_regex, mgm_reg, cflags );
+            free( mgm_reg );
+            if( m_error != 0 ) regfree( &m_regex );
+#else
+            m_regex = g_regex_new( mgm_reg, GRegexCompileFlags( cflags ), GRegexMatchFlags( 0 ), &m_error );
+            free( mgm_reg );
+            if( m_regex == NULL ) g_clear_error( &m_error );
+#endif
+            else goto compile_done;
         }
     }
-    else{
-#endif
+#endif //HAVE_MIGEMO_H
 
-    if( regcomp( &m_reg, asc_reg, cflags ) != 0 ){
-        regfree( &m_reg );
+#if USE_REGEX_COMPAT
+    m_error = regcomp( &m_regex, asc_reg, cflags );
+    if( m_error != 0 ){
+        regfree( &m_regex );
+        if( target_asc ) free( target_asc );
         return false;
     }
+#else
+    m_regex = g_regex_new( asc_reg, GRegexCompileFlags( cflags ), GRegexMatchFlags( 0 ), &m_error );
+    if( m_regex == NULL ){
+        if( target_asc ) free( target_asc );
+        return false;
+    }
+#endif
 
 #ifdef HAVE_MIGEMO_H
-    }
+compile_done:
 #endif
 
     m_compiled = true;
+    if( target_asc ) free( target_asc );
     return true;
 }
 
 
-const bool Regex::exec( const std::string& target, const size_t offset )
+std::string RegexPattern::errstr() const
 {
-    regmatch_t pmatch[ REGEX_MAX_NMATCH ];
+    std::string errmsg;
 
-    memset(pmatch, 0, sizeof(pmatch));
+#if USE_REGEX_COMPAT
+    switch( m_error ){
+        case 0: errmsg = "エラーはありません。"; break;
+        case REG_BADBR: errmsg = "無効な後方参照オペレータを使用しています。"; break;
+        case REG_BADPAT: errmsg = "無効なグループやリストなどを使用しています。"; break;
+        case REG_BADRPT: errmsg = "'*' が最初の文字としてくるような、無効な繰り返しオペレータを使用しています。"; break;
+        case REG_EBRACE: errmsg = "インターバルオペレータ {} が閉じていません。"; break;
+        case REG_EBRACK: errmsg = "リストオペレータ [] が閉じていません。"; break;
+        case REG_ECOLLATE: errmsg = "照合順序の要素として有効ではありません。"; break;
+        case REG_ECTYPE: errmsg = "未知のキャラクタークラス名です。"; break;
+#ifdef HAVE_REGEX_H
+        case REG_EEND: errmsg = "未定義エラー。POSIX.2 には定義されていません。"; break;
+#endif
+        case REG_EESCAPE: errmsg = "正規表現がバックスラッシュで終っています。"; break;
+        case REG_EPAREN: errmsg = "グループオペレータ () が閉じていません。"; break;
+        case REG_ERANGE: errmsg = "無効な範囲オペレータを使用しています。"; break;
+        case REG_ESIZE: errmsg = "複雑過ぎる正規表現です。"; break;
+        case REG_ESPACE: errmsg = "regex ルーチンがメモリーを使い果たしました。"; break;
+        case REG_ESUBREG: errmsg = "サブエクスプレッション (...) への無効な後方参照です。"; break;
+        default: errmsg = "未知のエラーが発生しました。"; break;
+    }
+#else
+    errmsg = m_error->message;
+#endif
+    return errmsg;
+}
 
-    if ( ! m_compiled ) return false;
 
-    if( target.empty() ) return false;
-    if( target.length() <= offset ) return false;
+///////////////////////////////////////////////
+
+Regex::Regex()
+    : m_target_asc( NULL ),
+      m_table_pos( NULL )
+{
+    m_results.clear();
+    m_pos.clear();
+}
+
+
+Regex::~Regex()
+{
+    if( m_target_asc ) free( m_target_asc );
+    if( m_table_pos ) free( m_table_pos );
+}
 
+
+bool Regex::match( const RegexPattern& creg, const std::string& target, const size_t offset, const bool notbol, const bool noteol )
+{
     m_pos.clear();
     m_results.clear();
 
+    if ( ! creg.m_compiled ) return false;
+
+    if( target.empty() ) return false;
+    if( target.length() <= offset ) return false;
+
     const char* asc_target = target.c_str() + offset;
 
     bool exec_asc = false;
 
+    // Unicode正規化
+    if( creg.m_norm ){
+
+#ifdef _DEBUG
+        std::cout << "Regex::match offset = " << offset << std::endl;
+        std::cout << target << std::endl;
+#endif
+
+        if( ! m_target_asc ) m_target_asc = ( char* )malloc( MAX_TARGET_SIZE );
+        if( ! m_table_pos ) m_table_pos = ( int* )malloc( MAX_TARGET_SIZE * sizeof( int ) );
+
+        MISC::norm( asc_target, m_target_asc, m_table_pos, MAX_TARGET_SIZE );
+        exec_asc = true;
+        asc_target = m_target_asc;
+
+#ifdef _DEBUG
+        std::cout << m_target_asc << std::endl;
+#endif
+    }
+
     // 全角英数字 → 半角英数字、半角カナ → 全角カナ
-    if( m_wchar && MISC::has_widechar( asc_target ) ){
+    else if( creg.m_wchar && MISC::has_widechar( asc_target ) ){
 
 #ifdef _DEBUG
-        std::cout << "Regex::exec offset = " << offset << std::endl;
+        std::cout << "Regex::match offset = " << offset << std::endl;
         std::cout << target << std::endl;
 #endif
 
         if( ! m_target_asc ) m_target_asc = ( char* )malloc( MAX_TARGET_SIZE );
-        if( ! m_table_pos ) m_table_pos = ( int* )malloc( MAX_TARGET_SIZE );
+        if( ! m_table_pos ) m_table_pos = ( int* )malloc( MAX_TARGET_SIZE * sizeof( int ) );
 
         MISC::asc( asc_target, m_target_asc, m_table_pos, MAX_TARGET_SIZE );
         exec_asc = true;
@@ -160,15 +273,30 @@
 #endif
     }
 
-#ifdef USE_ONIG    
+#if USE_REGEX_COMPAT
+    regmatch_t pmatch[ REGEX_MAX_NMATCH ];
+    memset( pmatch, 0, sizeof( pmatch ));
+
+    int eflags = 0;
+    if( notbol ) eflags |= REG_NOTBOL;
+    if( noteol ) eflags |= REG_NOTEOL;
+#else
+    GMatchInfo *pmatch;
+
+    int eflags = 0;
+    if( notbol ) eflags |= G_REGEX_MATCH_NOTBOL;
+    if( noteol ) eflags |= G_REGEX_MATCH_NOTEOL;
+#endif
+
+#ifdef HAVE_ONIGPOSIX_H
 
     // 鬼車はnewlineを無視するようなので、文字列のコピーを取って
     // 改行をスペースにしてから実行する
-    if( ! m_newline ){
+    if( ! creg.m_newline ){
 
         std::string target_copy = asc_target;
         for( size_t i = 0; i < target_copy.size(); ++i ) if( target_copy[ i ] == '\n' ) target_copy[ i ] = ' ';
-        if( regexec( &m_reg, target_copy.c_str(), REGEX_MAX_NMATCH, pmatch, 0 ) != 0 ){
+        if( regexec( &creg.m_regex, target_copy.c_str(), REGEX_MAX_NMATCH, pmatch, eflags ) != 0 ){
             return false;
         }
     }
@@ -176,58 +304,93 @@
 
 #endif
 
-    if( regexec( &m_reg, asc_target, REGEX_MAX_NMATCH, pmatch, 0 ) != 0 ){
+#if USE_REGEX_COMPAT
+    if( regexec( &creg.m_regex, asc_target, REGEX_MAX_NMATCH, pmatch, eflags ) != 0 ){
         return false;
     }
+    const int match_count = REGEX_MAX_NMATCH;
+#else
+    if( ! g_regex_match( creg.m_regex, asc_target, GRegexMatchFlags( eflags ), &pmatch ) ){
+        g_match_info_free( pmatch );
+        return false;
+    }
+    const int match_count = g_match_info_get_match_count( pmatch ) + 1;
+#endif
 
-    for( int i = 0; i < REGEX_MAX_NMATCH; ++i ){
+    for( int i = 0; i < match_count; ++i ){
+
+        int so, eo;
+#if USE_REGEX_COMPAT
+        so = pmatch[ i ].rm_so;
+        eo = pmatch[ i ].rm_eo;
+#else
+        if( ! g_match_info_fetch_pos( pmatch, i, &so, &eo ) ) so = eo = -1;
+#endif
+
+        if( so < 0 || eo < 0 ){
+            m_pos.push_back( so );
+            m_results.push_back( std::string() );
+        }
 
-        int so = pmatch[ i ].rm_so;
-        int eo = pmatch[ i ].rm_eo;
-        if( exec_asc && so >= 0 && eo >= 0 ){
+        else{
+            if( exec_asc ){
 #ifdef _DEBUG
-            std::cout << "so = " << so << " eo = " << eo;
+                std::cout << "so=" << so << " eo=" << eo;
 #endif
-            so = m_table_pos[ so ];
-            eo = m_table_pos[ eo ];
+                while( so > 0 && m_table_pos[ so ] < 0 ) so--;
+                so = m_table_pos[ so ];
+                while( m_table_pos[ eo ] < 0 ) eo++;
+                eo = m_table_pos[ eo ];
 #ifdef _DEBUG
-            std::cout << " -> so = " << so << " eo = " << eo << std::endl;
+                std::cout << " -> so=" << so << " eo=" << eo << std::endl;
 #endif
-        }
-        so += offset;
-        eo += offset;
-
-        m_pos.push_back( so );
+            }
+            so += offset;
+            eo += offset;
 
-        if( so >= 0 && eo >= 0 ) m_results.push_back( target.substr( so, eo - so ) );
-        else m_results.push_back( std::string() );
+            m_pos.push_back( so );
+            m_results.push_back( target.substr( so, eo - so ) );
+        }
     }
     
+#if !USE_REGEX_COMPAT
+    g_match_info_free( pmatch );
+#endif
+
     return true;
 }
 
 
-// icase : 大文字小文字区別しない
-// newline :  . に改行をマッチさせない
-// usemigemo : migemo使用 (コンパイルオプションで指定する必要あり)
-// wchar : 全角半角の区別をしない
-const bool Regex::exec( const std::string reg, const std::string& target,
-                        const size_t offset, const bool icase, const bool newline, const bool use_migemo, const bool wchar )
+//
+// マッチした文字列と $0〜$9 or \0〜\9 を置換する
+//
+std::string Regex::replace( const std::string& repstr )
 {
-    if ( ! compile(reg, icase, newline, use_migemo, wchar ) ) return false;
+    if( repstr.empty() ) return repstr;
 
-    if ( ! exec(target, offset) ){
-        dispose();
-        return false;
+    const char* p0 = repstr.c_str();
+    const char* p1;
+    std::string str_out;
+
+    while( ( p1 = strchr( p0, '\\' ) ) != NULL ){
+        int n = p1[ 1 ] - '0';
+        str_out.append( p0, p1 - p0 );
+        p0 = p1 + 2;
+        if( n < 0 || n > 9 ){
+            str_out.push_back( p1[ 1 ] );
+        }
+        else if( m_results.size() > ( size_t )n && m_pos[ n ] != -1 ){
+            str_out += m_results[ n ];
+        }
     }
     
-    dispose();
+    str_out.append( repstr, p0 - repstr.c_str(), std::string::npos );
     
-    return true;
+    return str_out;
 }
 
 
-const std::string Regex::str( const size_t num )
+const std::string Regex::str( const size_t num ) const
 {
     if( m_results.size() > num  ) return m_results[ num ];
 
@@ -235,7 +398,7 @@
 }
 
 
-const int Regex::pos( const size_t num )
+int Regex::pos( const size_t num ) const
 {
     if( m_results.size() > num ) return m_pos[ num ];
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdregex.h jd-2.8.9-a171004/src/jdlib/jdregex.h
--- jd-2.8.9-150226/src/jdlib/jdregex.h	2012-03-03 14:31:40.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdregex.h	2016-06-28 20:21:21.869305106 +0900
@@ -10,25 +10,61 @@
 #include "config.h"
 #endif
 
-#ifdef USE_ONIG
+#ifdef HAVE_ONIGPOSIX_H
 #include <onigposix.h>
-#elif defined( USE_PCRE )
+#elif defined( HAVE_PCREPOSIX_H )
 #include <pcreposix.h>
-#else
+#elif defined( HAVE_REGEX_H )
 #include <regex.h>
+#else
+#include <glib.h>
 #endif	/** USE_ONIG **/
 
+#define USE_REGEX_COMPAT ( defined( HAVE_ONIGPOSIX_H ) || defined( HAVE_PCREPOSIX_H ) || defined( HAVE_REGEX_H ) )
 
 namespace JDLIB
 {
-    class Regex
+    class Regex;
+
+    class RegexPattern
     {
-        std::vector< int > m_pos;
-        std::vector< std::string > m_results;
-        regex_t m_reg;
+        friend class Regex;
+
+#if USE_REGEX_COMPAT
+        regex_t m_regex;
+#else
+        GRegex *m_regex;
+#endif
         bool m_compiled;
         bool m_newline;
         bool m_wchar;
+        bool m_norm;
+#if USE_REGEX_COMPAT
+        int m_error;
+#else
+        GError *m_error;
+#endif
+
+    public:
+        RegexPattern() : m_compiled( false ), m_error( 0 ){};
+        RegexPattern( const std::string& reg, const bool icase, const bool newline,
+                        const bool usemigemo = false, const bool wchar = false,
+                        const bool norm = false );
+        ~RegexPattern();
+
+        bool set( const std::string& reg, const bool icase, const bool newline,
+                        const bool usemigemo = false, const bool wchar = false,
+                        const bool norm = false );
+        void clear();
+        bool compiled() const { return m_compiled; }
+        std::string errstr() const;
+    };
+
+
+    class Regex
+    {
+        std::vector< int > m_pos;
+        std::vector< std::string > m_results;
 
         char *m_target_asc;
         int *m_table_pos;
@@ -38,21 +74,26 @@
         Regex();
         ~Regex();
 
-        void dispose();
+        // notbol : 行頭マッチは必ず失敗する
+        // noteol : 行末マッチは必ず失敗する
+        bool match( const RegexPattern& creg, const std::string& target, const size_t offset, const bool notbol = false, const bool noteol = false );
         
         // icase : 大文字小文字区別しない
         // newline :  . に改行をマッチさせない
         // usemigemo : migemo使用 (コンパイルオプションで指定する必要あり)
         // wchar : 全角半角の区別をしない
-        const bool compile( const std::string reg
-                            , const bool icase, const bool newline, const bool usemigemo, const bool wchar );
+        bool exec( const std::string& reg, const std::string& target, const size_t offset,
+                    const bool icase, const bool newline, const bool usemigemo = false,
+                    const bool wchar = false, const bool norm = false ){
+            RegexPattern pattern( reg, icase, newline, usemigemo, wchar, norm );
+            return match( pattern, target, offset );
+        }
 
-        const bool exec( const std::string& target, const size_t offset );
-        const bool exec( const std::string reg, const std::string& target, const size_t offset
-                         , const bool icase, const bool newline, const bool usemigemo, const bool wchar );
+        // マッチした文字列と \0〜\9 を置換する
+        std::string replace( const std::string& repstr );
 
-        const int pos( const size_t num );
-        const std::string str( const size_t num );
+        int pos( const size_t num ) const;
+        const std::string str( const size_t num ) const;
     };
 }
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdsocket.cpp jd-2.8.9-a171004/src/jdlib/jdsocket.cpp
--- jd-2.8.9-150226/src/jdlib/jdsocket.cpp	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdsocket.cpp	2017-02-12 04:04:57.358438000 +0900
@@ -0,0 +1,564 @@
+// ライセンス: GPL2
+
+#ifdef _WIN32
+#  ifndef WINVER
+#    define WINVER 0x0501 // require Windows XP or Server 2003 for getaddrinfo
+#  endif
+#endif
+
+//#define _DEBUG
+#include "jddebug.h"
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef _WIN32
+#  include <windows.h>
+#  include <winsock2.h>
+#  include <ws2tcpip.h>
+#  ifdef HAVE_WSPIAPI_H
+#    include <wspiapi.h>
+#  endif
+#  include <process.h>
+#else
+#  include <arpa/inet.h>
+#  include <sys/socket.h>
+#  include <netdb.h>
+#endif
+
+#include "jdsocket.h"
+#include "miscmsg.h"
+#include "miscutil.h"
+
+#include "config/globalconf.h"
+
+
+#ifdef _WIN32
+inline bool SOC_ISVALID( unsigned int _soc ){ return _soc != INVALID_SOCKET; }
+#else
+inline bool SOC_ISVALID( int _soc ){ return _soc >= 0; }
+#define INVALID_SOCKET -1
+#endif
+
+
+
+enum{
+    SOCKET_OK = 0,
+    SOCKET_ERR = -1,
+};
+
+inline bool is_ipaddress( const char* addr )
+{
+#ifdef _WIN32
+    struct sockaddr_storage buf;
+    int size = sizeof( buf );
+
+    if( !WSAStringToAddressA( (LPSTR)addr, AF_INET, NULL, (LPSOCKADDR)&buf, &size )
+            || !WSAStringToAddressA( (LPSTR)addr, AF_INET6, NULL, (LPSOCKADDR)&buf, &size ) ){
+        return true;
+    }
+#else
+    unsigned char buf[ sizeof( struct in6_addr) ];
+
+    if( inet_aton( addr, (struct in_addr *)&buf )
+            || inet_pton( AF_INET6, addr, &buf ) ){
+        return true;
+    }
+#endif
+    return false;
+}
+
+
+inline std::string cafile()
+{
+    std::string capath = CONFIG::get_root_cafile();
+#ifdef _WIN32
+    if( capath[ 0 ] == '/' ){
+        char buf[ MAX_PATH ];
+        char *pos;
+        GetModuleFileName( NULL, buf, MAX_PATH );
+        if( ( pos = strrchr( buf, '\\' ) ) ){
+            *pos = '\0';
+            if( ( pos = strrchr( buf, '\\' ) ) && memcmp( pos, "\\bin\0", 5 ) == 0 ){
+                capath = std::string( buf, pos - buf )
+                    + MISC::replace_str( capath, "/", "\\" );
+            }
+        }
+    }
+#endif
+    return capath;
+}
+
+
+#if defined (USE_GNUTLS)
+#  include "jdsocketgnutls.cpp"
+#elif defined (USE_OPENSSL)
+#  include "jdsocketopenssl.cpp"
+#elif defined (USE_NSS)
+#  include "jdsocketnss.cpp"
+#endif
+
+
+using namespace JDLIB;
+
+
+Socket::Socket( bool *const stop, const bool async )
+    : m_stop( stop )
+    , m_async( async )
+    , m_tout( 0 )
+    , m_soc( INVALID_SOCKET )
+    , m_tls( 0 )
+{
+    // nothing to do
+}
+
+
+Socket::~Socket()
+{
+    if( SOC_ISVALID( m_soc ) ){
+        close();
+    }
+}
+
+
+bool Socket::connect( const std::string& hostname, const std::string& port, const bool use_ipv6 )
+{
+#ifdef _DEBUG
+    std::cout << "Socket::connect" << std::endl;
+#endif
+
+    bool ret = false;
+    int optval = 0;
+    socklen_t optlen = sizeof( int );
+
+    // addrinfo 取得
+    struct addrinfo hints, *ainf=0;
+    memset( &hints, 0, sizeof( addrinfo ) );
+    if( use_ipv6 ){
+        hints.ai_family = AF_UNSPEC;
+#ifndef _WIN32
+        hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
+#endif
+    }
+    else hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+
+    int err = getaddrinfo( hostname.c_str(), port.c_str(), &hints, &ainf );
+    if( err ) {
+        m_errmsg = "getaddrinfo failed: " + hostname + ":" + port;
+        goto EXIT_CONNECT;
+    }
+
+#ifdef _DEBUG
+    std::cout << "host=" << hostname
+              << " ip=" << inet_ntoa( ( ( sockaddr_in* )( ainf->ai_addr ) )->sin_addr ) << std::endl;
+#endif
+
+    // ソケット作成
+    m_soc = socket( ainf->ai_family, ainf->ai_socktype, ainf->ai_protocol );
+    if( ! SOC_ISVALID( m_soc ) ){
+        m_errmsg = "socket failed";
+        goto EXIT_CONNECT;
+    }
+
+    // ソケットを非同期に設定
+    if( m_async ){
+#ifdef _WIN32
+        unsigned long flags = 0;
+        if ( ioctlsocket( m_soc, FIONBIO, &flags) != 0 )
+#else
+        int flags;
+        flags = fcntl( m_soc, F_GETFL, 0);
+        if( flags == -1 || fcntl( m_soc, F_SETFL, flags | O_NONBLOCK ) < 0 )
+#endif
+        {
+            m_errmsg = "fcntl failed";
+            goto EXIT_CONNECT;
+        }
+    }
+
+    // connect peer
+    err = ::connect( m_soc, ainf->ai_addr, ainf->ai_addrlen );
+    if( err ){
+
+        // ノンブロックでまだ接続中
+#ifdef _WIN32
+        if ( ! m_async || WSAGetLastError() != WSAEWOULDBLOCK )
+#else
+        if ( ! m_async || errno != EINPROGRESS )
+#endif
+        {
+            m_errmsg = "connect failed: " + hostname + ":" + port;
+            goto EXIT_CONNECT;
+        }
+    }
+
+    // connect待ち
+    if( ! wait_fds( false ) ){
+        m_errmsg = "connect timeout";
+        goto EXIT_CONNECT;
+    }
+
+    // connectが成功したかチェック
+    if(
+#ifdef _WIN32
+        getsockopt( m_soc, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen ) != 0
+#else
+        getsockopt( m_soc, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen ) < 0
+#endif
+        || optval ){
+        m_errmsg = "connect(getsockopt) failed: " + hostname + ":" + port;
+        goto EXIT_CONNECT;
+    }
+
+    ret = true;
+
+EXIT_CONNECT:
+    // addrinfo開放
+    if( ainf ) freeaddrinfo( ainf );
+
+    return ret;
+}
+
+
+
+bool Socket::socks_handshake( const std::string& hostname, const std::string& port, const int protocol )
+{
+#ifdef _DEBUG
+    std::cout << "Socket::socks_handshake" << std::endl;
+#endif
+
+    bool ret = false;
+
+    // addrinfo 取得
+    uint32_t addr = 0;
+    if( protocol == PROXY_SOCKS4 /*|| protocol == PROXY_SOCKS5*/ ){
+        struct addrinfo hints, *ainf=0;
+        memset( &hints, 0, sizeof( addrinfo ) );
+        // XXX SOCKS5対応の時にIPv6もサポートが必要
+            hints.ai_family = AF_INET;
+        hints.ai_socktype = SOCK_STREAM;
+
+        int err = getaddrinfo( hostname.c_str(), port.c_str(), &hints, &ainf );
+        if( err ) {
+            m_errmsg = "getaddrinfo failed: " + hostname + ":" + port;
+            return ret;
+        }
+
+#ifdef _DEBUG
+        std::cout << "host=" << hostname
+                  << " ip=" << inet_ntoa( ( ( sockaddr_in* )( ainf->ai_addr ) )->sin_addr ) << std::endl;
+#endif
+
+        addr = *(uint32_t*)&( ( sockaddr_in* )( ainf->ai_addr ) )->sin_addr;
+        freeaddrinfo( ainf );
+    }
+
+    // ポート番号変換
+    int num_port = atoi( port.c_str() );
+    if( num_port < 0 || num_port >= 0x10000 ){
+        m_errmsg = "invlid port number: " + hostname + ":" + port;
+        return ret;
+    }
+
+    char msgbuf[ 100 ];
+    char *p = msgbuf;
+    switch( protocol ){
+
+    case PROXY_SOCKS4:
+        *p++ = 4;   // SOCKS Version
+        *p++ = 1;   // TCP/IP Stream
+        *p++ = ( num_port >> 8 ) & 0xff;
+        *p++ = num_port & 0xff;
+        *p++ = addr & 0xff;
+        *p++ = ( addr >> 8 ) & 0xff;
+        *p++ = ( addr >> 16 ) & 0xff;
+        *p++ = ( addr >> 24 ) & 0xff;
+        *p++ = 0;
+        break;
+
+    case PROXY_SOCKS4A:
+        *p++ = 4;   // SOCKS Version
+        *p++ = 1;   // TCP/IP Stream
+        *p++ = ( num_port >> 8 ) & 0xff;
+        *p++ = num_port & 0xff;
+        *p++ = 0;
+        *p++ = 0;
+        *p++ = 0;
+        *p++ = 1;
+        *p++ = 0;
+        for( size_t i = 0; i <= hostname.length(); ++i )
+            *p++ = *( hostname.c_str() + i );
+        break;
+    default:
+        // XXX SOCK5未対応
+        return ret;
+    }
+
+    if( write( msgbuf, p - msgbuf ) <= 0 ){
+        return ret;
+    }
+
+    if( read( msgbuf, 8 ) <= 0 || msgbuf[0] != 0 || msgbuf[1] != 0x5a ){
+
+        m_errmsg = "socks handshake failed";
+
+#ifdef _DEBUG
+        std::cout << "socks recieve: " << std::hex << std::setfill('0')
+            << std::setw(2) << (unsigned char)msgbuf[0] << " "
+            << std::setw(2) << (unsigned char)msgbuf[1] << " "
+            << std::setw(2) << (unsigned char)msgbuf[2] << " "
+            << std::setw(2) << (unsigned char)msgbuf[3] << " ip="
+            << std::setw(2) << (unsigned char)msgbuf[4] << " "
+            << std::setw(2) << (unsigned char)msgbuf[5] << " "
+            << std::setw(2) << (unsigned char)msgbuf[6] << " "
+            << std::setw(2) << (unsigned char)msgbuf[7] << std::endl;
+#endif
+        return ret;
+    }
+
+#ifdef _DEBUG
+    std::cout << "socks handshake done: port=" << std::hex << std::setfill('0')
+            << std::setw(2) << msgbuf[2] << " " << std::setw(2) << msgbuf[3]
+            << " ip=" << std::setw(2) << msgbuf[4] << " " << std::setw(2) << msgbuf[5]
+            << " " << std::setw(2) << msgbuf[6] << " " << std::setw(2) << msgbuf[7]
+            << std::endl;
+#endif
+    ret = true;
+
+    return ret;
+}
+
+
+
+
+void Socket::close()
+{
+#ifdef _DEBUG
+    std::cout << "Socket::close" << std::endl;
+#endif
+
+    m_errmsg = std::string();
+
+    if( m_tls ){
+
+        tls_close();
+    }
+
+    if( SOC_ISVALID( m_soc ) ){
+
+        // writefds待ち
+        // 待たないとclose()したときにfinパケットが消える?
+        wait_fds( false );
+
+        // 送信禁止
+#ifdef _WIN32
+        shutdown( m_soc, SD_SEND );
+#else
+        shutdown( m_soc, SHUT_WR );
+#endif
+
+        wait_fds( false );
+
+        // ソケットクローズ
+#ifdef _WIN32
+        closesocket( m_soc );
+#else
+        ::close( m_soc );
+#endif
+
+        m_soc = INVALID_SOCKET;
+    }
+}
+
+
+
+int Socket::write( const char* buf, const size_t bufsize )
+{
+#ifdef _DEBUG
+    std::cout << "Socket::write  size=" << bufsize << std::endl;
+#endif
+
+    int ret = 0;
+
+    size_t send_size = bufsize;
+    while( send_size > 0 ){
+
+        ssize_t tmpsize;
+
+        if( m_tls ){
+            // TLS使用
+            tmpsize = tls_write( buf + bufsize - send_size, send_size );
+            if( !m_errmsg.empty() ){
+                ret = SOCKET_ERR;
+                goto EXIT_WRITE;
+            }
+        }
+        else if( SOC_ISVALID( m_soc ) ){
+
+            // writefds 待ち
+            if( ! wait_fds( false ) ){
+                m_errmsg = "send timeout";
+                ret = SOCKET_ERR;
+                goto EXIT_WRITE;
+            }
+
+#ifdef _WIN32
+            tmpsize = send( m_soc, buf + bufsize - send_size, send_size, 0 );
+            int lastError = WSAGetLastError();
+#else
+#ifdef MSG_NOSIGNAL
+            tmpsize = send( m_soc, buf + bufsize - send_size, send_size, MSG_NOSIGNAL );
+#else
+            // SolarisにはMSG_NOSIGNALが無いのでSIGPIPEをIGNOREする (FreeBSD4.11Rにもなかった)
+            signal( SIGPIPE , SIG_IGN ); /* シグナルを無視する */
+            tmpsize = send( m_soc, buf + bufsize - send_size, send_size, 0 );
+            signal(SIGPIPE,SIG_DFL); /* 念のため戻す */
+#endif // MSG_NOSIGNAL
+#endif // _WIN32
+
+#ifdef _WIN32
+            if( tmpsize == 0
+                || ( tmpsize < 0 && !( lastError == WSAEWOULDBLOCK || lastError == WSAEINTR ) ) )
+#else
+            if( tmpsize == 0
+                || ( tmpsize < 0 && !( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) ) )
+#endif
+            {
+#ifdef _WIN32
+                if( lastError == WSAECONNRESET ) m_errmsg = "connection reset by peer";
+                else m_errmsg = "send failed: error=" + MISC::itostr( lastError );
+#else
+                if( errno == ECONNRESET ) m_errmsg = "connection reset by peer";
+                else m_errmsg = "send failed: errno=" + MISC::itostr( errno );
+#endif
+                ret = SOCKET_ERR;
+                goto EXIT_WRITE;
+            }
+        }
+        else{
+            m_errmsg = "send failed: internal error";
+            ret = SOCKET_ERR;
+            goto EXIT_WRITE;
+        }
+
+        if( tmpsize > 0 ) send_size -= tmpsize;
+
+        if( *m_stop ) goto EXIT_WRITE;
+    }
+
+    ret = bufsize - send_size;
+
+EXIT_WRITE:
+    return ret;
+}
+
+
+
+int Socket::read( char* buf, const size_t bufsize )
+{
+    ssize_t ret = 0;
+
+    do {
+        if( m_tls ){
+            // SSL
+            ret = tls_read( buf, bufsize );
+            if( !m_errmsg.empty() ){
+                ret = SOCKET_ERR;
+                goto EXIT_READ;
+            }
+        }
+        else if( SOC_ISVALID( m_soc ) ){
+
+            // readfds 待ち
+            if( !wait_fds( true ) ){
+                m_errmsg = "receive timeout";
+                ret = SOCKET_ERR;
+                goto EXIT_READ;
+            }
+
+            ret = recv( m_soc, buf, bufsize, 0 );
+#ifdef _WIN32
+            int lastError = WSAGetLastError();
+            if( ret < 0 && !( lastError == WSATRY_AGAIN || lastError == WSAEWOULDBLOCK || lastError == WSAEINTR ) )
+#else
+            if( ret < 0 && !( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) )
+#endif
+            {
+#ifdef _WIN32
+                if( lastError == WSAECONNRESET ) m_errmsg = "connection reset by peer";
+                else m_errmsg = "receive failed: error=" + MISC::itostr( lastError );
+#else
+                if( errno == ECONNRESET ) m_errmsg = "connection reset by peer";
+                else m_errmsg = "receive failed: errno=" + MISC::itostr( errno );
+#endif
+                ret = SOCKET_ERR;
+                goto EXIT_READ;
+            }
+        }
+        else{
+            m_errmsg = "receive failed: internal error";
+            ret = SOCKET_ERR;
+            goto EXIT_READ;
+        }
+
+        if( *m_stop ) goto EXIT_READ;
+
+    } while( ret < 0 );
+
+#ifdef _DEBUG
+    std::cout << "Socket::recv  size=" << ret << std::endl;
+#endif
+
+EXIT_READ:
+    return ret;
+}
+
+
+//
+// sent, recv待ち
+//
+bool Socket::wait_fds( const bool fdrecv )
+{
+    if( ! m_soc || ! m_async ) return true;
+
+    int count = 0;
+    for(;;){
+
+        errno = 0;
+
+        int ret;
+        fd_set fdset;
+        FD_ZERO( &fdset );
+        FD_SET( m_soc, &fdset );
+        timeval tv = { 1, 0 };
+
+        if( fdrecv ) ret = select( m_soc + 1, &fdset, NULL, NULL, &tv );
+        else ret = select( m_soc + 1, NULL, &fdset, NULL, &tv );
+
+#ifdef _DEBUG
+//        if( errno == EINTR && ret < 0 ) std::cout << "Socket::wait_fds : errno = EINTR" << std::endl;
+#endif
+        if( errno != EINTR && ret < 0 ){
+#ifdef _DEBUG
+            std::cout << "Socket::wait_fds: errno = " << errno << std::endl;
+#endif
+            MISC::ERRMSG( "select failed" );
+            break;
+        }
+
+        if( errno != EINTR && FD_ISSET( m_soc, &fdset ) ) return true;
+        if( *m_stop ) break;
+
+        if( ++count >= m_tout ) break;
+#ifdef _DEBUG
+        std::cout << "Socket::wait_fds" << ( fdrecv ? "(recv)" : "(send)" ) << " timeout = " << count << std::endl;
+#endif
+    }
+
+    return false;
+}
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdsocket.h jd-2.8.9-a171004/src/jdlib/jdsocket.h
--- jd-2.8.9-150226/src/jdlib/jdsocket.h	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdsocket.h	2016-05-10 02:19:50.023467000 +0900
@@ -0,0 +1,91 @@
+// ライセンス: GPL2
+
+
+#ifndef _JDSOCKET_H
+#define _JDSOCKET_H
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#if defined (USE_GNUTLS)
+#  include <gnutls/gnutls.h>
+#elif defined (USE_OPENSSL)
+#  include <openssl/ssl.h>
+// gdkmm/device.h で定義される set_key マクロと衝突する
+#  ifdef set_key
+#    undef set_key
+#  endif
+#elif defined (USE_NSS)
+#  include <nspr/prio.h>
+#endif
+
+#include <string>
+
+namespace JDLIB
+{
+    enum{
+        PROXY_NONE = 0,
+        PROXY_HTTP,
+        PROXY_SOCKS4,
+        PROXY_SOCKS4A,
+        PROXY_SOCKS5,
+        PROXY_SOCKS5H
+    };
+
+    class Socket
+    {
+        std::string m_errmsg;
+        std::string m_tls_proto;
+        volatile bool *m_stop;
+        bool m_async;
+        long m_tout;
+        int m_soc;
+
+#if defined (USE_GNUTLS)
+        gnutls_session_t m_tls;
+        bool m_terminated;
+
+#elif defined (USE_OPENSSL)
+        SSL_CTX* m_ctx;
+        SSL* m_tls;
+
+#elif defined (USE_NSS)
+        PRFileDesc *m_tls;
+
+#else
+#error No TLS library specified.
+#endif
+
+    public:
+
+        Socket( bool *const stop, const bool async = true );
+        ~Socket();
+
+        std::string& get_errmsg(){ return m_errmsg; }
+        void set_timeout( const long timeout ){ m_tout = timeout; }
+
+        bool connect( const std::string& host, const std::string& port, const bool use_ipv6 );
+        void close();
+
+        int write( const char* buf, const size_t bufsize );
+        int read( char* buf, const size_t bufsize );
+
+        bool tls_handshake( const std::string& hostname, const bool verify );
+        bool socks_handshake( const std::string& hostname, const std::string& port, const int protocol );
+
+    private:
+        int tls_write( const char* buf, const size_t length );
+        int tls_read( char* buf, const size_t bufsize );
+        void tls_close();
+
+        bool wait_fds( const bool fdrecv );
+    };
+
+
+    void tlslib_init();
+    void tlslib_deinit();
+}
+
+#endif
+
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdsocketgnutls.cpp jd-2.8.9-a171004/src/jdlib/jdsocketgnutls.cpp
--- jd-2.8.9-150226/src/jdlib/jdsocketgnutls.cpp	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdsocketgnutls.cpp	2016-04-27 19:59:03.361525000 +0900
@@ -0,0 +1,319 @@
+// ライセンス: GPL2
+
+
+#include <gnutls/x509.h>
+
+#if GNUTLS_VERSION_NUMBER < 0x020000
+#define gnutls_certificate_credentials_t gnutls_certificate_credentials
+#endif // GNUTLS_VERSION_NUMBER >= 0x020000
+
+#if GNUTLS_VERSION_NUMBER < 0x029900
+#define GNUTLS_E_PREMATURE_TERMINATION GNUTLS_E_UNEXPECTED_PACKET_LENGTH
+#endif
+
+
+using namespace JDLIB;
+
+void JDLIB::tlslib_init()
+{
+#ifdef _DEBUG
+    std::cout << "tlslib_init" << std::endl;
+#endif
+
+#ifdef _WIN32
+    WSADATA wsaData;
+    if ( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 ){
+        MISC::ERRMSG( "could not startup winsock2" );
+    }
+#endif
+
+    gnutls_global_init();
+}
+
+
+void JDLIB::tlslib_deinit()
+{
+#ifdef _DEBUG
+    std::cout << "tlslib_deinit" << std::endl;
+#endif
+
+    gnutls_global_deinit();
+
+#ifdef _WIN32
+    WSACleanup();
+#endif
+}
+
+
+
+bool Socket::tls_handshake( const std::string& hostname, const bool verify )
+{
+#ifdef _DEBUG
+    std::cout << "Socket::tls_handshake" << std::endl;
+#endif
+
+    bool ret = false;
+    int err = 0;
+
+    if( !SOC_ISVALID( m_soc ) ){
+        m_errmsg = "invalid socket";
+        return ret;
+    }
+
+    if( m_tls != NULL ){
+        m_errmsg = "duplicate fuction call";
+        return ret;
+    }
+
+    m_terminated = false;
+    gnutls_certificate_credentials_t cred = 0;
+
+    gnutls_init( &m_tls, GNUTLS_CLIENT );
+
+#if GNUTLS_VERSION_NUMBER >= 0x020108
+    // gnutls >= 2.1.7 (unreleased)
+    const char* const priority ="NORMAL:%COMPAT";
+    if( ( err = gnutls_priority_set_direct( m_tls, priority, NULL ) ) != GNUTLS_E_SUCCESS ){
+        m_errmsg = "priority_set failed: ";
+        m_errmsg += gnutls_strerror( err );
+        goto EXIT_TLS_CONNECT;
+    }
+#else // GNUTLS_VERSION_NUMBER >= 0x020108
+    static const int priority_prot[] = { GNUTLS_SSL3, 0 };
+    // DEPRECATED (gnutls >= 2.1.4 gnutls =< 2.1.6)
+    // UNDEPRECATED (gnutls >= 2.1.7)
+    gnutls_set_default_priority( m_tls );
+    // _GNUTLS_GCC_ATTR_DEPRECATE (gnutls >= 2.12.0)
+    gnutls_protocol_set_priority( m_tls, priority_prot );
+#endif // GNUTLS_VERSION_NUMBER >= 0x020108
+
+    // allow the use of private ciphersuites. Server Name Indication (SNI)
+    if( !is_ipaddress( hostname.c_str() ) ){
+         gnutls_server_name_set( m_tls, GNUTLS_NAME_DNS, hostname.c_str(), hostname.length() );
+    }
+
+    if( ( err = gnutls_certificate_allocate_credentials( &cred ) ) != GNUTLS_E_SUCCESS ){
+        m_errmsg = "allocate cred failed: ";
+        m_errmsg += gnutls_strerror( err );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    if( verify && ( err = gnutls_certificate_set_x509_trust_file( cred, cafile().c_str(), GNUTLS_X509_FMT_PEM ) ) < 0 ){
+        m_errmsg = "set trust file failed: ";
+        m_errmsg += gnutls_strerror( err );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    if( ( err = gnutls_credentials_set( m_tls, GNUTLS_CRD_CERTIFICATE, cred ) ) != GNUTLS_E_SUCCESS ){
+        m_errmsg = "cred set failed: ";
+        m_errmsg += gnutls_strerror( err );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    gnutls_transport_set_ptr( m_tls, (gnutls_transport_ptr_t)m_soc );
+
+#if GNUTLS_VERSION_NUMBER >= 0x030100
+    gnutls_handshake_set_timeout( m_tls, m_tout * 1000 );
+#endif
+
+    while ( ( err = gnutls_handshake( m_tls ) ) != GNUTLS_E_SUCCESS )
+    {
+        if ( gnutls_error_is_fatal( err ) != 0 ){
+            m_errmsg = "handshake failed: ";
+            m_errmsg += gnutls_strerror( err );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        // writefds 待ち
+        if( ! wait_fds( true ) ){
+            m_errmsg = "handshake timeout";
+            goto EXIT_TLS_CONNECT;
+        }
+    }
+
+    if( verify ){
+        unsigned int status;
+        gnutls_x509_crt_t cert;
+        const gnutls_datum_t* cert_list;
+        unsigned int cert_list_size;
+
+        // try to verify the peer's certificate
+        if( ( err = gnutls_certificate_verify_peers2( m_tls, &status ) ) < 0 ){
+            m_errmsg = "certificate verify failed: ";
+            m_errmsg += gnutls_strerror( err );
+            goto EXIT_TLS_CONNECT;
+        }
+        else if( status & GNUTLS_CERT_INVALID ){
+            m_errmsg = "certificate is not trusted";
+            goto EXIT_TLS_CONNECT;
+        }
+        else if( status & GNUTLS_CERT_SIGNER_NOT_FOUND ){
+            m_errmsg = "certificate hasn't got a known issuer";
+            goto EXIT_TLS_CONNECT;
+        }
+        else if( status & GNUTLS_CERT_REVOKED ){
+            m_errmsg = "certificate has been revoked";
+            goto EXIT_TLS_CONNECT;
+        }
+#if GNUTLS_VERSION_NUMBER >= 0x020800
+        else if( status & GNUTLS_CERT_NOT_ACTIVATED ){
+            m_errmsg = "certificate not activated yet";
+            goto EXIT_TLS_CONNECT;
+        }
+        else if( status & GNUTLS_CERT_EXPIRED ){
+            m_errmsg = "certificate has expired";
+            goto EXIT_TLS_CONNECT;
+        }
+#endif
+
+        // initialize an X.509 certificate structure.
+        if( ( err = gnutls_x509_crt_init( &cert ) ) < GNUTLS_E_SUCCESS ){
+            m_errmsg = "cert init failed";
+            m_errmsg += gnutls_strerror( err );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        if( ( cert_list = gnutls_certificate_get_peers( m_tls, &cert_list_size ) ) == NULL ){
+            m_errmsg = "No cert was found";
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        // XXX We only check the first certificate in the given chain.
+        if( ( err = gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER ) ) < GNUTLS_E_SUCCESS ){
+            m_errmsg = "error parsing certificate: ";
+            m_errmsg += gnutls_strerror( err );
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        // check if the given certificate's subject matches the given hostname.
+        if( ! gnutls_x509_crt_check_hostname( cert, hostname.c_str() ) ){
+            m_errmsg = "the cert's owner does not match target hostname '";
+            m_errmsg += hostname + "'";
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+
+#if GNUTLS_VERSION_NUMBER < 0x020800
+        // Check for time-based validity
+        time_t expire = gnutls_x509_crt_get_expiration_time( cert );
+
+        if( expire == (time_t)-1 ){
+            m_errmsg = "cert expiration date verify failed";
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+        else if( expire < time( NULL ) ){
+            m_errmsg = "cert expiration date has passed";
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        time_t activate = gnutls_x509_crt_get_activation_time( cert );
+
+        if( activate == (time_t)-1 ){
+            m_errmsg = "cert activation date verify failed";
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+        else if( activate > time( NULL ) ){
+            m_errmsg = "cert not activated yet";
+            gnutls_x509_crt_deinit( cert );
+            goto EXIT_TLS_CONNECT;
+        }
+#endif
+
+        gnutls_x509_crt_deinit( cert );
+    }
+
+
+#ifdef _DEBUG
+    std::cout << gnutls_protocol_get_name( gnutls_protocol_get_version( m_tls ) )
+        << " " << gnutls_cipher_suite_get_name( gnutls_kx_get( m_tls ),
+                        gnutls_cipher_get( m_tls ), gnutls_mac_get( m_tls ) )
+        << " (compression:" << gnutls_compression_get_name( gnutls_compression_get( m_tls ) )
+        << ") connected." << std::endl;
+#endif
+
+    ret = true;
+
+EXIT_TLS_CONNECT:
+
+    if( cred ) gnutls_certificate_free_credentials( cred );
+
+    return ret;
+}
+
+
+
+void Socket::tls_close()
+{
+    int ret;
+
+    while( ( ret = gnutls_bye( m_tls, GNUTLS_SHUT_RDWR ) ) != GNUTLS_E_SUCCESS ){
+
+        if( ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED ){
+            if( !m_terminated )
+                MISC::ERRMSG( std::string("TLS shutdown failed: ") + gnutls_strerror( ret ) );
+            break;
+        }
+
+        // readfds 待ち
+        if( !wait_fds( false ) ){
+            MISC::ERRMSG( "TLS shutdown timeout" );
+            break;
+        }
+    }
+
+    gnutls_deinit( m_tls );
+    m_terminated = false;
+    m_tls = NULL;
+}
+
+int Socket::tls_write( const char* buf, const size_t length )
+{
+    int tmpsize = gnutls_record_send( m_tls, buf, length );
+
+    if( tmpsize < 0 ){
+        if( tmpsize != GNUTLS_E_AGAIN ){
+            m_errmsg = "tls_write failed: ";
+            m_errmsg += gnutls_strerror( tmpsize );
+        }
+
+        // readfds 待ち
+        else if( !wait_fds( false ) ){
+            m_errmsg = "send timeout";
+        }
+    }
+
+    return tmpsize;
+}
+
+
+int Socket::tls_read( char* buf, const size_t bufsize )
+{
+    int ret = 0;
+    if( !m_terminated ){
+
+        ret = gnutls_record_recv( m_tls, buf, bufsize );
+        if( ret == GNUTLS_E_PREMATURE_TERMINATION ){
+            ret = 0;
+            m_terminated = true;
+        }
+        else if( ret < 0 ){
+            if( ret != GNUTLS_E_AGAIN ){
+                m_errmsg = "tls_read failed: ";
+                m_errmsg += gnutls_strerror( ret );
+            }
+
+            // readfds 待ち
+            else if( !wait_fds( true ) ){
+                m_errmsg = "receive timeout";
+            }
+        }
+    }
+
+    return ret;
+}
+
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdsocketnss.cpp jd-2.8.9-a171004/src/jdlib/jdsocketnss.cpp
--- jd-2.8.9-150226/src/jdlib/jdsocketnss.cpp	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdsocketnss.cpp	2016-04-27 19:58:36.161525000 +0900
@@ -0,0 +1,277 @@
+// ライセンス: GPL2
+
+
+// NSPR include files
+#include <nspr/prerror.h>
+#include <nspr/prinit.h>
+// Private API, to turn a POSIX file descriptor into an NSPR handle.
+#include <nspr/private/pprio.h>
+
+// NSS include files
+#include <nss/nss.h>
+#include <nss/secmod.h>
+#include <nss/ssl.h>
+#include <nss/sslproto.h>
+#include <nss/sslerr.h>
+
+
+using namespace JDLIB;
+
+void JDLIB::tlslib_init()
+{
+#ifdef _DEBUG
+    std::cout << "tlslib_init" << std::endl;
+#endif
+
+#ifdef _WIN32
+    WSADATA wsaData;
+    if ( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 ){
+        MISC::ERRMSG( "could not startup winsock2" );
+    }
+#endif
+
+    PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 32 );
+    NSS_NoDB_Init( 0 );
+    SSL_OptionSetDefault( SSL_NO_CACHE, PR_TRUE );
+    //SSL_OptionSetDefault( SSL_ENABLE_SESSION_TICKETS, PR_TRUE );
+    NSS_SetDomesticPolicy();
+
+    // Initialize the trusted certificate store.
+    const char *const module_name = "library=libnssckbi.so name=\"Root Certs\"";
+    SECMODModule *module = SECMOD_LoadUserModule( (char *)module_name, NULL, PR_FALSE );
+    if( module == NULL || !module->loaded ) {
+        std::string errmsg = "NSPR: ";
+        errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+                //PR_ErrorToName( PR_GetError() );
+        MISC::ERRMSG( errmsg );
+        if( module ) SECMOD_DestroyModule( module );
+    }
+}
+
+
+void JDLIB::tlslib_deinit()
+{
+#ifdef _DEBUG
+    std::cout << "tlslib_deinit" << std::endl;
+#endif
+
+    SSL_ClearSessionCache();
+    NSS_Shutdown();
+    PR_Cleanup();
+
+#ifdef _WIN32
+    WSACleanup();
+#endif
+}
+
+
+static SECStatus PR_CALLBACK cert_hook( void *arg, PRFileDesc *fd )
+{
+    (void)fd;
+    (void)arg;
+    return SECSuccess;
+}
+
+
+
+bool Socket::tls_handshake( const std::string& hostname, const bool verify )
+{
+#ifdef _DEBUG
+    std::cout << "Socket::tls_handshake" << std::endl;
+#endif
+
+    bool ret = false;
+
+    if( !SOC_ISVALID( m_soc ) ){
+        m_errmsg = "invalid socket";
+        return ret;
+    }
+
+    if( m_tls != NULL ){
+        m_errmsg = "duplicate fuction call";
+        return ret;
+    }
+
+
+#ifdef SSL_LIBRARY_VERSION_TLS_1_2
+    SSLVersionRange sslver = { SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_2 };
+#elif defined SSL_LIBRARY_VERSION_TLS_1_1
+    SSLVersionRange sslver = { SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_1 };
+#else
+    SSLVersionRange sslver = { SSL_LIBRARY_VERSION_3_0, SSL_LIBRARY_VERSION_TLS_1_0 };
+#endif
+
+    PRFileDesc *prfd;
+
+    // setup sockets
+    if( !( prfd = PR_ImportTCPSocket( m_soc ) ) ){
+        m_errmsg = "Unable to convert socket: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    if( m_async ){
+        PRSocketOptionData sock_opt;
+        sock_opt.option = PR_SockOpt_Nonblocking;
+        sock_opt.value.non_blocking = PR_TRUE;
+        if( PR_SetSocketOption( prfd, &sock_opt ) != PR_SUCCESS ){
+            m_errmsg = "Unable to non blocking connection :";
+            m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+            goto EXIT_TLS_CONNECT;
+        }
+    }
+
+    if( !( m_tls = SSL_ImportFD( NULL, prfd ) ) ){
+        m_errmsg = "Unable to import SSL FileDesc: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+    prfd = NULL;
+
+    // setup SSL Options
+    if( SSL_OptionSet( m_tls, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE ) != SECSuccess ){
+        m_errmsg = "Unable to setup handshake mode: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+    if( SSL_OptionSet( m_tls, SSL_ENABLE_FDX, PR_TRUE ) != SECSuccess ){
+        m_errmsg = "Unable to setup full duplex mode: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+    if( !verify && SSL_BadCertHook( m_tls, cert_hook, NULL ) != SECSuccess ){
+        m_errmsg = "Unable to register certificate check hook: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+    if( SSL_VersionRangeSet( m_tls, &sslver) != SECSuccess ){
+        m_errmsg = "Unable to register protocol version range: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    // start SSL/TLS negotiation
+    if( SSL_ResetHandshake( m_tls, PR_FALSE ) != SECSuccess ){
+        m_errmsg = "Unable to reset secure handshake: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    if( !is_ipaddress( hostname.c_str() ) &&
+            SSL_SetURL( m_tls, hostname.c_str() ) != SECSuccess ){
+        m_errmsg = "Unable to register target host: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    // Make the handshake happen now
+    while( SSL_ForceHandshakeWithTimeout( m_tls, PR_SecondsToInterval( m_tout ) ) != SECSuccess ){
+        // PR_WOULD_BLOCK_ERROR in non-blocking mode is not an error
+        if( PR_GetError() != PR_WOULD_BLOCK_ERROR ){
+            m_errmsg = "Unable to secure negotiation: ";
+            m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        // writefds待ち
+        if( ! wait_fds( true ) ){
+            m_errmsg = "handshake timeout";
+            goto EXIT_TLS_CONNECT;
+        }
+    }
+
+#ifdef _DEBUG
+    SSLChannelInfo channel;
+    if( SSL_GetChannelInfo( m_tls, &channel, sizeof channel ) != SECSuccess ||
+        channel.length != sizeof channel || ! channel.cipherSuite ){
+        m_errmsg = "Unable to get ChannelInfo: ";
+        m_errmsg += PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT );
+        goto EXIT_TLS_CONNECT;
+    }
+
+    const char *tlsver;
+    switch( channel.protocolVersion ){
+        case SSL_LIBRARY_VERSION_2: tlsver = "SSLv2"; break;
+        case SSL_LIBRARY_VERSION_3_0: tlsver = "SSLv3"; break;
+        case SSL_LIBRARY_VERSION_TLS_1_0: tlsver = "TLS1.0"; break;
+#ifdef SSL_LIBRARY_VERSION_TLS_1_1
+        case SSL_LIBRARY_VERSION_TLS_1_1: tlsver = "TLS1.1"; break;
+#endif
+#ifdef SSL_LIBRARY_VERSION_TLS_1_2
+        case SSL_LIBRARY_VERSION_TLS_1_2: tlsver = "TLS1.2"; break;
+#endif
+#ifdef SSL_LIBRARY_VERSION_TLS_1_3
+        case SSL_LIBRARY_VERSION_TLS_1_3: tlsver = "TLS1.3"; break;
+#endif
+        default: tlsver = "SSL/TLS";
+    }
+
+    SSLCipherSuiteInfo suite;
+    if( SSL_GetCipherSuiteInfo( channel.cipherSuite, &suite, sizeof suite ) == SECSuccess )
+        std::cout << tlsver << " " << suite.cipherSuiteName << " connected." << std::endl;
+    else std::cout << tlsver << " connected." << std::endl;
+#endif
+
+    ret = true;
+
+EXIT_TLS_CONNECT:
+
+    if( prfd ) PR_Close( prfd );
+
+    return ret;
+}
+
+
+void Socket::tls_close()
+{
+    if( PR_Shutdown( m_tls, PR_SHUTDOWN_BOTH ) != PR_SUCCESS ){
+        MISC::ERRMSG( std::string( "TLS shutdown failed: " )
+                + PR_ErrorToString( PR_GetError(), PR_LANGUAGE_I_DEFAULT ) );
+    }
+    PR_Close( m_tls );
+    m_soc = INVALID_SOCKET;
+    m_tls = NULL;
+}
+
+
+int Socket::tls_write( const char* buf, const size_t length )
+{
+    ssize_t tmpsize = PR_Write( m_tls, buf, length );
+
+    if( tmpsize < 0 ){
+        PRErrorCode err = PR_GetError();
+        if( err != PR_WOULD_BLOCK_ERROR ){
+            m_errmsg = "PR_Write failed: ";
+            m_errmsg += PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT );
+        }
+
+        // writefds 待ち
+        else if( ! wait_fds( true ) ){
+            m_errmsg = "send timeout";
+        }
+    }
+
+    return tmpsize;
+}
+
+
+int Socket::tls_read( char* buf, const size_t bufsize )
+{
+    ssize_t ret = PR_Read( m_tls, buf, bufsize );
+
+    if( ret < 0 ){
+        PRErrorCode err = PR_GetError();
+        if( err != PR_WOULD_BLOCK_ERROR ){
+            m_errmsg = "PR_Read failed: ";
+            m_errmsg += PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT );
+        }
+
+        // writefds 待ち
+        else if( ! wait_fds( true ) ){
+            m_errmsg = "receive timeout";
+        }
+    }
+
+    return ret;
+}
+
diff -aurNZ jd-2.8.9-150226/src/jdlib/jdsocketopenssl.cpp jd-2.8.9-a171004/src/jdlib/jdsocketopenssl.cpp
--- jd-2.8.9-150226/src/jdlib/jdsocketopenssl.cpp	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/jdsocketopenssl.cpp	2016-04-27 20:41:16.925486000 +0900
@@ -0,0 +1,245 @@
+// ライセンス: GPL2
+
+#include <openssl/err.h>
+#include <openssl/conf.h>
+
+using namespace JDLIB;
+
+void JDLIB::tlslib_init()
+{
+#ifdef _DEBUG
+    std::cout << "tlslib_init" << std::endl;
+#endif
+
+#ifdef _WIN32
+    WSADATA wsaData;
+    if ( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 ){
+        MISC::ERRMSG( "could not startup winsock2" );
+    }
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100003L
+    SSL_library_init();
+#else
+    OPENSSL_init_ssl( 0, NULL );
+    OPENSSL_no_config();
+    OpenSSL_add_ssl_algorithms();
+#endif
+    SSL_load_error_strings();
+}
+
+
+void JDLIB::tlslib_deinit()
+{
+#ifdef _DEBUG
+    std::cout << "tlslib_deinit" << std::endl;
+#endif
+
+    EVP_cleanup();
+    ERR_free_strings();
+
+#ifdef _WIN32
+    WSACleanup();
+#endif
+}
+
+
+inline const char* ssllib_errstr()
+{
+    return ERR_reason_error_string( ERR_get_error() );
+}
+
+static std::string tls_get_errstr( const int error ){
+    std::string str_err;
+
+    switch( error ){
+    case SSL_ERROR_NONE: str_err = "no error"; break;
+    case SSL_ERROR_SSL: str_err = ssllib_errstr(); break;
+    case SSL_ERROR_SYSCALL: str_err = "syscall errno=" + MISC::itostr( errno ); break;
+    case SSL_ERROR_ZERO_RETURN: str_err = "zero return"; break;
+    case SSL_ERROR_WANT_CONNECT: str_err = "want connect"; break;
+    case SSL_ERROR_WANT_ACCEPT: str_err = "want accept"; break;
+    case SSL_ERROR_WANT_X509_LOOKUP: str_err = "want x509 lookup"; break;
+    }
+
+    return str_err;
+}
+
+
+bool Socket::tls_handshake( const std::string& hostname, const bool verify )
+{
+#ifdef _DEBUG
+    std::cout << "Socket::tls_handshake" << std::endl;
+#endif
+
+    bool ret = false;
+    int err = 0;
+
+    if( !SOC_ISVALID( m_soc ) ){
+        m_errmsg = "invalid socket";
+        goto EXIT_TLS_CONNECT;
+    }
+
+    if( m_tls != NULL ){
+        m_errmsg = "duplicate fuction call";
+        goto EXIT_TLS_CONNECT;
+    }
+
+
+    if( !( m_ctx = SSL_CTX_new( SSLv23_client_method() ) ) ){
+        m_errmsg = "SSL_CTX_new failed: ";
+        m_errmsg += ssllib_errstr();
+        goto EXIT_TLS_CONNECT;
+    }
+
+    //SSL_CTX_set_options( m_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 );
+
+    if( verify ){
+        // load the trusted client CA certificate into context
+        if( SSL_CTX_load_verify_locations( m_ctx, cafile().c_str(), NULL) != 1 ){
+            m_errmsg = "load_verify_location failed: ";
+            m_errmsg += ssllib_errstr();
+            goto EXIT_TLS_CONNECT;
+        }
+
+        SSL_CTX_set_verify( m_ctx, SSL_VERIFY_PEER, NULL );
+        SSL_CTX_set_verify_depth( m_ctx, 4 );
+    }
+
+    if( !m_tout ) SSL_CTX_set_timeout( m_ctx, m_tout );
+
+    if( !( m_tls = SSL_new( m_ctx ) ) ){
+        m_errmsg = "SSL_new failed: ";
+        m_errmsg += ssllib_errstr();
+        goto EXIT_TLS_CONNECT;
+    }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+    if( !is_ipaddress( hostname.c_str() ) &&
+            ( err = SSL_set_tlsext_host_name( m_tls, hostname.c_str() ) ) != 1 ){
+        m_errmsg = "set_tlsext_host_name failed: ";
+        m_errmsg += ssllib_errstr();
+        goto EXIT_TLS_CONNECT;
+    }
+#endif
+
+    if( !( err = SSL_set_fd( m_tls, m_soc ) ) ){
+        m_errmsg = "set_fd failed: ";
+        m_errmsg += ssllib_errstr();
+        goto EXIT_TLS_CONNECT;
+    }
+
+    SSL_set_connect_state( m_tls );
+
+    while( ( err = SSL_do_handshake( m_tls ) ) != 1 ){
+        bool want_read;
+
+        err = SSL_get_error( m_tls, err );
+        if( err == SSL_ERROR_WANT_READ ) want_read = true;
+        else if( err == SSL_ERROR_WANT_WRITE ) want_read = false;
+        else{
+            m_errmsg = "SSL_do_handshake failed: ";
+            m_errmsg += tls_get_errstr( err );
+            goto EXIT_TLS_CONNECT;
+        }
+
+        // writefds 待ち
+        if( ! wait_fds( want_read ) ){
+            m_errmsg = "SSL_do_handshake timeout";
+            goto EXIT_TLS_CONNECT;
+        }
+    }
+
+#ifdef _DEBUG
+    std::cout << SSL_get_version( m_tls ) << " (OpenSSL)" << std::endl;;
+#endif
+
+    ret = true;
+
+EXIT_TLS_CONNECT:
+
+    return ret;
+}
+
+
+void Socket::tls_close()
+{
+    int ret;
+
+    while( ( ret = SSL_shutdown( m_tls ) ) != 1 ){
+        bool want_read = true;
+
+        if( ret != 0 ){
+            ret = SSL_get_error( m_tls, ret );
+            if( ret == 0 || ret == SSL_ERROR_WANT_READ ) want_read = true;
+            else if( ret == SSL_ERROR_WANT_WRITE ) want_read = false;
+            else{
+                if( ret != SSL_ERROR_SYSCALL || errno != 0 ){
+                    MISC::ERRMSG( "SSL_shutdown failed: " + tls_get_errstr( ret ) );
+                }
+                break;
+            }
+        }
+
+        // writefds 待ち
+        if( ! wait_fds( want_read ) ){
+            MISC::ERRMSG( "SSL_shutdown timeout" );
+            break;
+        }
+    }
+    SSL_free( m_tls );
+    SSL_CTX_free( m_ctx );
+
+    m_tls = NULL;
+}
+
+
+int Socket::tls_write( const char* buf, const size_t length )
+{
+    ssize_t tmpsize = SSL_write( m_tls, buf, length );
+
+    if( tmpsize < 0 ){
+        bool want_read;
+
+        int ret = SSL_get_error( m_tls, tmpsize );
+        if( ret == SSL_ERROR_WANT_READ ) want_read = true;
+        else if( ret == SSL_ERROR_WANT_WRITE ) want_read = false;
+        else{
+            m_errmsg = "SSL_write failed: " + tls_get_errstr( ret );
+            return tmpsize;
+        }
+
+        // writefds 待ち
+        if( ! wait_fds( want_read ) ){
+            m_errmsg = "send timeout";
+        }
+    }
+
+    return tmpsize;
+}
+
+
+int Socket::tls_read( char* buf, const size_t bufsize )
+{
+    ssize_t tmpsize = SSL_read( m_tls, buf, bufsize );
+
+    if( tmpsize < 0 ){
+        bool want_read;
+
+        int ret = SSL_get_error( m_tls, tmpsize );
+        if( ret == SSL_ERROR_WANT_READ ) want_read = true;
+        else if( ret == SSL_ERROR_WANT_WRITE ) want_read = false;
+        else{
+            m_errmsg = "SSL_read failed: " + tls_get_errstr( ret );
+            return tmpsize;
+        }
+
+        // writefds 待ち
+        if( ! wait_fds( want_read ) ){
+            m_errmsg = "receive timeout";
+        }
+    }
+
+    return tmpsize;
+}
+
diff -aurNZ jd-2.8.9-150226/src/jdlib/loader.cpp jd-2.8.9-a171004/src/jdlib/loader.cpp
--- jd-2.8.9-150226/src/jdlib/loader.cpp	2014-01-26 00:08:56.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/loader.cpp	2016-05-13 20:57:39.511669000 +0900
@@ -1,9 +1,5 @@
 // ライセンス: GPL2
 
-#ifdef _WIN32
-// require Windows XP or Server 2003 for getaddrinfo
-#define WINVER 0x0501
-#endif
 
 //#define _DEBUG
 //#define _DEBUG_CHUNKED
@@ -17,7 +13,7 @@
 #include "loader.h"
 #include "miscmsg.h"
 #include "miscutil.h"
-#include "ssl.h"
+#include "jdsocket.h"
 
 #ifdef _DEBUG_TIME
 #include "misctime.h"
@@ -32,25 +28,8 @@
 #include <sstream>
 #include <cstring>
 
-#include <errno.h>
-#include <fcntl.h>
-#ifdef _WIN32
-#include <process.h>
-#else
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#endif
-#include <signal.h>
-
 #include <glibmm.h>
 
-#ifdef _WIN32
-// _soc : SOCKET (unsigned int)
-#define SOC_ISVALID(_soc) ( (_soc) != INVALID_SOCKET )
-#else
-// _soc : int
-#define SOC_ISVALID(_soc) ( (_soc) >= 0 )
-#endif
 
 enum
 {
@@ -61,10 +40,6 @@
 };
 
 
-#ifdef _WIN32
-bool initialized_loader = false;
-#endif
-
 
 //
 // トークンとスレッド起動待ちキュー
@@ -244,12 +219,6 @@
         MISC::ERRMSG( "queue of loaders are not empty." );
         assert( false );
     }
-
-#ifdef _WIN32
-    if ( initialized_loader ){
-        WSACleanup();
-    }
-#endif
 }
 
 
@@ -263,8 +232,7 @@
 // low_priority = true の時はスレッド起動待ち状態になった時に、起動順のプライオリティを下げる
 //
 Loader::Loader( const bool low_priority )
-    : m_addrinfo( 0 ),
-      m_stop( false ),
+    : m_stop( false ),
       m_loading( false ),
       m_low_priority( low_priority ),
       m_buf( 0 ),
@@ -276,16 +244,6 @@
     std::cout << "Loader::Loader : loader was created.\n";
 #endif
 
-#ifdef _WIN32
-    if ( !initialized_loader ){
-        WSADATA wsaData;
-        if ( WSAStartup(MAKEWORD(2,0), &wsaData) != 0 ){
-            MISC::ERRMSG( "could not startup winsock2" );
-        }
-        initialized_loader = true;
-    }
-#endif
-
     clear();
 }
 
@@ -424,60 +382,67 @@
     }
     
     m_data.host = m_data.url.substr( i, i2 - i );
-    i = i2; m_data.path = m_data.url.substr( i2 );
+    m_data.path = m_data.url.substr( i2 );
 
     // ポートセット
 
+    // プロトコルを見て自動決定
+    // http
+    if( m_data.protocol == "http://" ) m_data.port = 80;
+
+    // https
+    else if( m_data.protocol == "https://" ){
+        m_data.use_ssl = true;
+        m_data.port = 443;
+    }
+
+    // その他
+    else{
+        m_data.code = HTTP_ERR;
+        m_data.str_code = "unknown protocol : " + m_data.url;
+        MISC::ERRMSG( m_data.str_code );
+        return false;
+    }
+
+    // ポート番号
+    if( data_in.port != 0 ) m_data.port = data_in.port;
+
     // ホスト名の後に指定されている
     if( ( i = m_data.host.find( ":" ) ) != std::string::npos ){
         m_data.port = atoi( m_data.host.substr( i+1 ).c_str() );
         m_data.host = m_data.host.substr( 0, i );
     }
 
-    // 明示的に指定
-    else if( data_in.port != 0 ) m_data.port = data_in.port;
-
-    // プロトコルを見て自動決定
-    else{
-
-        // http
-        if( m_data.protocol.find( "http://" ) != std::string::npos ) m_data.port = 80;
-
-        // https
-        else if( m_data.protocol.find( "https://" ) != std::string::npos ){
-            m_data.use_ssl = true;
-            m_data.async = false;
-            m_data.port = 443;
-        }
+    // 明示的にssl使用指定
+    if( data_in.use_ssl ) m_data.use_ssl = true;
 
-        // その他
+    // プロキシ
+    m_data.host_proxy = data_in.host_proxy;
+    if( ( i = m_data.host_proxy.find( "://" ) ) != std::string::npos ){
+        const std::string proto = data_in.host_proxy.substr( 0, i );
+        if( proto == "http" ) m_data.protocol_proxy = PROXY_HTTP;
+        else if( proto == "socks4" ) m_data.protocol_proxy = PROXY_SOCKS4;
+        else if( proto == "socks4a" ) m_data.protocol_proxy = PROXY_SOCKS4A;
         else{
-
             m_data.code = HTTP_ERR;
-            m_data.str_code = "unknown protocol : " + m_data.url;
+            m_data.str_code = "unknown proxy protocol : " + proto;
             MISC::ERRMSG( m_data.str_code );
             return false;
         }
+        m_data.host_proxy = m_data.host_proxy.substr( i + 3 );
     }
+    else m_data.protocol_proxy = PROXY_HTTP;
 
-    // 明示的にssl使用指定
-    if( data_in.use_ssl ){
-        m_data.use_ssl = true;
-        m_data.async = false;
-        m_data.port = 443;
-    }
-
-    // プロキシ
-    m_data.host_proxy = data_in.host_proxy;
+    // プロキシのポート番号
+    if( data_in.port_proxy != 0 ) m_data.port_proxy = data_in.port_proxy;
 
-    // 先頭に *tp:// が付いていたら取り除く
-    if( ! m_data.host_proxy.empty() && m_data.host_proxy.find( "tp://" ) != std::string::npos ){
-        const bool protocol = false;
-        m_data.host_proxy = MISC::get_hostname( m_data.host_proxy , protocol );
+    // ホスト名の後に指定されている
+    if( ( i = m_data.host_proxy.rfind( ":" ) ) != std::string::npos ){
+        m_data.port_proxy = atoi( m_data.host_proxy.substr( i + 1 ).c_str() );
+        m_data.host_proxy = m_data.host_proxy.substr( 0, i );
     }
+
     if( ! m_data.host_proxy.empty() ){
-        m_data.port_proxy = data_in.port_proxy;
-        if( m_data.port_proxy == 0 ) m_data.port_proxy = 8080;
         m_data.basicauth_proxy = data_in.basicauth_proxy;
     }
 
@@ -493,7 +458,6 @@
     m_data.timeout = MAX( TIMEOUT_MIN, data_in.timeout );
     m_data.ex_field = data_in.ex_field;
     m_data.basicauth = data_in.basicauth;
-    m_data.use_ipv6 = data_in.use_ipv6;
 
 #ifdef _DEBUG    
     std::cout << "host: " << m_data.host << std::endl;
@@ -506,6 +470,7 @@
     std::cout << "agent: " << m_data.agent << std::endl;
     std::cout << "referer: " << m_data.referer << std::endl;
     std::cout << "cookie: " << m_data.cookie_for_write << std::endl;
+    std::cout << "protocol of proxy: " << m_data.protocol_proxy << std::endl;
     std::cout << "proxy: " << m_data.host_proxy << std::endl;
     std::cout << "port of proxy: " << m_data.port_proxy << std::endl;
     std::cout << "proxy basicauth : " << m_data.basicauth_proxy << std::endl;
@@ -566,15 +531,8 @@
     // エラーメッセージ
     std::string errmsg;
 
-#ifdef _WIN32
-    SOCKET soc = INVALID_SOCKET; // ソケットID
-#else
-    int soc = -1; // ソケットID
-#endif
     bool use_proxy = ( ! m_data.host_proxy.empty() );
 
-    JDLIB::JDSSL* ssl = NULL;
-    
     // 送信メッセージ作成
     const std::string msg_send = create_msg_send();
     
@@ -584,238 +542,125 @@
     std::cout <<"send :----------\n" <<  msg_send << "\n---------------\n";
 #endif
 
-    // addrinfo 取得
+    bool async = ! m_data.use_ssl || CONFIG::get_tls_nonblocking();
+    JDLIB::Socket soc( &m_stop, async );
+    soc.set_timeout( m_data.timeout );
+
+    // socket接続
+    const bool use_ipv6 = CONFIG::get_use_ipv6();
     if( m_data.host_proxy.empty() ){
-        m_addrinfo = get_addrinfo( m_data.host, m_data.port );
-        if( ! m_addrinfo ){
+        if( ! soc.connect( m_data.host, MISC::itostr( m_data.port ), use_ipv6 ) ){
             m_data.code = HTTP_ERR;
-            errmsg = "getaddrinfo failed : " + m_data.url;
+            errmsg = soc.get_errmsg();
             goto EXIT_LOADING;
         }
     }
     else{
-        m_addrinfo = get_addrinfo( m_data.host_proxy, m_data.port_proxy );
-        if( ! m_addrinfo ){
+        if( ! soc.connect( m_data.host_proxy, MISC::itostr( m_data.port_proxy ), use_ipv6 ) ){
             m_data.code = HTTP_ERR;
-            errmsg = "getaddrinfo failed : " + m_data.host_proxy;
+            errmsg = soc.get_errmsg();
             goto EXIT_LOADING;
         }
     }
 
-    // ソケット作成
-    soc = socket( m_addrinfo ->ai_family, m_addrinfo ->ai_socktype, m_addrinfo ->ai_protocol );
-    if( ! SOC_ISVALID( soc ) ){
-        m_data.code = HTTP_ERR;
-        errmsg = "socket failed : " + m_data.url;
-        goto EXIT_LOADING;
-    }
-
-    // ソケットを非同期に設定
-    if( m_data.async ){
-#ifdef _WIN32
-        u_long flags = 0;
-        if ( ioctlsocket( soc, FIONBIO, &flags) != 0 ){
-#else
-        int flags;
-        flags = fcntl( soc, F_GETFL, 0);
-        if( flags == -1 || fcntl( soc, F_SETFL, flags | O_NONBLOCK ) < 0 ){
-#endif
-            m_data.code = HTTP_ERR;
-            errmsg = "fcntl failed";
-            goto EXIT_LOADING;
-        }
-    }
+    // 受信用バッファの割り当て
+    assert( m_buf == NULL );
+    m_buf = ( char* )malloc( m_lng_buf );
     
-    // サーバにconnect
-    int ret;
-    ret = connect( soc, m_addrinfo ->ai_addr, m_addrinfo ->ai_addrlen );
-    if( ret != 0 ){
-
-        // ノンブロックでまだ接続中
-#ifdef _WIN32
-        if ( !( m_data.async && WSAGetLastError() == WSAEWOULDBLOCK ) ){
-#else
-        if ( !( m_data.async && errno == EINPROGRESS ) ){
-#endif
+    // Socksのハンドシェーク
+    if( use_proxy && m_data.protocol_proxy != PROXY_HTTP ){
 
+        if( ! soc.socks_handshake( m_data.host, MISC::itostr( m_data.port ), m_data.protocol_proxy ) ){
             m_data.code = HTTP_ERR;
-            if( ! use_proxy ) errmsg = "connect failed : " + m_data.host;
-            else errmsg = "connect failed : " + m_data.host_proxy;
+            errmsg = soc.get_errmsg();
             goto EXIT_LOADING;
         }
     }
 
-    // connect待ち
-    if( m_data.async ){
+    // HTTP tunneling
+    else if( use_proxy && m_data.protocol_proxy == PROXY_HTTP && m_data.use_ssl ){
 
-        if( ! wait_recv_send( soc, false ) ){
+        // CONNECT
+        std::string msg = "CONNECT ";
+        msg += m_data.host + ":" + MISC::itostr( m_data.port ) + " HTTP/1.1\r\n";
+        msg += "Host: " + m_data.host + "\r\n";
+        msg += "Proxy-Connection: keep-alive\r\n";
+        if( ! m_data.agent.empty() ) msg += "User-Agent: " + m_data.agent + "\r\n";
+        msg += "\r\n";
 
-            // タイムアウト
-            m_data.code = HTTP_TIMEOUT;
-            errmsg = "connect timeout";
-            goto EXIT_LOADING;
-        }
+        if( soc.write( msg.c_str(), msg.length() ) < 0 ){
 
-        // connectが成功したかチェック
-        int optval;
-        socklen_t optlen = sizeof( int );
-#ifdef _WIN32
-        if( getsockopt( soc, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen ) != 0 ){
-#else
-        if( getsockopt( soc, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen ) < 0 ){
-#endif
             m_data.code = HTTP_ERR;
-            errmsg = "getsockopt failed";
+            errmsg = soc.get_errmsg();
             goto EXIT_LOADING;
         }
 
-        if( optval != 0 ){
-            m_data.code = HTTP_ERR;
-            errmsg = "connect(getsockopt) failed";
-            goto EXIT_LOADING;
-        }
-
-#ifdef _DEBUG
-        std::cout << "connect ok\n";
-#endif
-    }
-
-    // ssl 初期化とコネクト
-    if( m_data.use_ssl ){
-
-        ssl = new JDLIB::JDSSL();
-        if( ! ssl->connect( soc ) ){
-            m_data.code = HTTP_ERR;
-            errmsg = ssl->get_errmsg() + " : " + m_data.url;
-            goto EXIT_LOADING;
-        }
-    }
-
-    // SEND 又は POST
-
-    // 通常
-    if( !ssl ){
-
-        size_t send_size = strlen( msg_send.data() );
-        while( send_size > 0 && !m_stop ){
-
-            // writefds 待ち
-            if( ! wait_recv_send( soc, false ) ){
+        // 読み込み
+        size_t read_size = 0;
+        while( read_size < m_lng_buf && !m_stop ){
 
-                // タイムアウト
-                m_data.code = HTTP_TIMEOUT;         
-                errmsg = "send timeout";
+            ssize_t tmpsize = soc.read( m_buf + read_size, m_lng_buf - read_size );
+            if( tmpsize < 0 ){
+                m_data.code = HTTP_ERR;
+                errmsg = soc.get_errmsg();
                 goto EXIT_LOADING;
             }
 
-            // SEND 又は POST
-#ifdef _WIN32
-            ssize_t tmpsize = send( soc, msg_send.data(), send_size,0);
-            int lastError = WSAGetLastError();
-#else
-#ifdef MSG_NOSIGNAL
-            ssize_t tmpsize = send( soc, msg_send.data(), send_size, MSG_NOSIGNAL );
-#else
-            // SolarisにはMSG_NOSIGNALが無いのでSIGPIPEをIGNOREする (FreeBSD4.11Rにもなかった)
-            signal( SIGPIPE , SIG_IGN ); /* シグナルを無視する */
-            ssize_t tmpsize = send( soc, msg_send.data(), send_size,0);
-            signal(SIGPIPE,SIG_DFL); /* 念のため戻す */
-#endif // MSG_NOSIGNAL
-#endif // _WIN32
-
-#ifdef _WIN32
-            if( tmpsize == 0
-                || ( tmpsize < 0 && !( lastError == WSAEWOULDBLOCK || errno == WSAEINTR ) ) ){
-#else
-            if( tmpsize == 0
-                || ( tmpsize < 0 && !( errno == EWOULDBLOCK || errno == EINTR ) ) ){
-#endif
+            else if( tmpsize == 0 ) goto EXIT_LOADING;
+            else read_size += tmpsize;
 
+            const int ret = receive_header( m_buf, read_size );
+            if( ret == HTTP_ERR ){
                 m_data.code = HTTP_ERR;
-                errmsg = "send failed : " + m_data.url;
+                errmsg = "invalid proxy header : " + m_data.url;
                 goto EXIT_LOADING;
             }
-
-            if( tmpsize > 0 ) send_size -= tmpsize;
+            else if( ret == HTTP_OK ){
+                if( m_data.code < 200 || m_data.code >= 300 ){
+                    goto EXIT_LOADING;
+                }
+                break;
+            }
         }
-
-        if( m_stop ) goto EXIT_LOADING;
-
-#ifdef _DEBUG
-        std::cout << "send ok\n";
-#endif
     }
 
-    // SSL使用
-    else{ 
-
-        if( ssl->write( msg_send.data(), strlen( msg_send.data() ) ) < 0 ){
+    // TLSのハンドシェーク
+    if( m_data.use_ssl ){
 
+        if( ! soc.tls_handshake( m_data.host.c_str(), CONFIG::get_verify_cert() ) ){
             m_data.code = HTTP_ERR;
-            errmsg = ssl->get_errmsg() + " : " + m_data.url;
+            errmsg = soc.get_errmsg();
             goto EXIT_LOADING;
         }
     }
 
-    // 受信用バッファを作ってメッセージ受信
-    size_t mrg;
-    mrg = 64; // 一応オーバーフロー避けのおまじない
-    assert( m_buf == NULL );
-    m_buf = ( char* )malloc( m_lng_buf + mrg );
+    // SEND 又は POST
+    if( soc.write( msg_send.c_str(), msg_send.length() ) < 0 ){
 
-    bool receiving_header;
+        m_data.code = HTTP_ERR;
+        errmsg = soc.get_errmsg();
+        goto EXIT_LOADING;
+    }
 
 #ifdef _DEBUG_TIME
     MISC::start_measurement( 1 );
 #endif
 
     // 受信開始
+    bool receiving_header;
     receiving_header = true;
     m_data.length_current = 0;
     m_data.size_data = 0;    
     do{
         // 読み込み
         size_t read_size = 0;
-        while( read_size < m_lng_buf - mrg && !m_stop ){
-
-            ssize_t tmpsize;
-
-            // 通常
-            if( !ssl ){
-
-#ifdef _DEBUG_TIME
-                MISC::start_measurement( 0 );
-#endif
-
-                // readfds 待ち
-                if( !wait_recv_send( soc, true ) ){
-                    // タイムアウト
-                    m_data.code = HTTP_TIMEOUT;         
-                    errmsg = "read timeout";
-                    goto EXIT_LOADING;
-                }
-
-                tmpsize = recv( soc, m_buf + read_size, m_lng_buf - read_size - mrg, 0 );
-                if( tmpsize < 0 && errno != EINTR ){
-                    m_data.code = HTTP_ERR;         
-                    errmsg = "recv() failed";
-                    goto EXIT_LOADING;
-                }
-
-#ifdef _DEBUG_TIME
-                std::cout << "size = " << tmpsize << " time = " << MISC::measurement( 0 ) << std::endl;
-#endif
-            }
+        while( read_size < m_lng_buf && !m_stop ){
 
-            // SSL
-            else{
-
-                tmpsize = ssl->read(  m_buf + read_size, m_lng_buf - read_size - mrg );
-                if( tmpsize < 0 ){
-                    m_data.code = HTTP_ERR;         
-                    errmsg = ssl->get_errmsg() + " : " + m_data.url;
-                    goto EXIT_LOADING;
-                }
+            ssize_t tmpsize = soc.read( m_buf + read_size, m_lng_buf - read_size );
+            if( tmpsize < 0 ){
+                m_data.code = HTTP_ERR;
+                errmsg = soc.get_errmsg();
+                goto EXIT_LOADING;
             }
 
             if( tmpsize == 0 ) break;
@@ -841,8 +686,6 @@
 
         }
 
-        m_buf[ read_size ] = '\0';
-
         // 停止指定
         if( m_stop ) break;
 
@@ -875,7 +718,7 @@
             if( !skip_chunk( m_buf, read_size ) ){
 
                 m_data.code = HTTP_ERR;
-                errmsg = "skip_chunk() failed : " + m_data.url;
+                errmsg = "skip_chunk() failed";
                 goto EXIT_LOADING;
             }
             if( ! read_size ) continue;
@@ -894,7 +737,7 @@
         else if( !unzip( m_buf, read_size ) ){
             
             m_data.code = HTTP_ERR;
-            errmsg = "unzip() failed : " + m_data.url;
+            errmsg = "unzip() failed";
             goto EXIT_LOADING;
         }
 
@@ -909,32 +752,6 @@
     // 終了処理
 EXIT_LOADING:
 
-    // ssl クローズ
-    if( ssl ){
-        ssl->close();
-        delete ssl;
-        ssl = NULL;
-    }
-
-    if( SOC_ISVALID( soc ) ){
-
-        // writefds待ち
-        // 待たないとclose()したときにfinパケットが消える?
-        if( ! wait_recv_send( soc, false ) ){
-
-            // タイムアウト
-            m_data.code = HTTP_TIMEOUT;         
-            errmsg = "send timeout";
-        }
-
-        // 送信禁止
-#ifdef _WIN32
-        shutdown( soc, SD_SEND );
-#else
-        shutdown( soc, SHUT_WR );
-#endif
-    }
-
     // 強制停止した場合
     if( m_stop ){
 #ifdef _DEBUG
@@ -952,17 +769,7 @@
     }
 
     // ソケットクローズ
-    if( SOC_ISVALID( soc ) ){
-#ifdef _WIN32
-        closesocket( soc );
-#else
-        close( soc );
-#endif
-    }
-
-    // addrinfo開放
-    if( m_addrinfo ) freeaddrinfo( m_addrinfo );
-    m_addrinfo = NULL;
+    soc.close();
 
     // トークン返す
     return_token( this );
@@ -980,47 +787,12 @@
 
 
 //
-// addrinfo 取得
-//
-struct addrinfo* Loader::get_addrinfo( const std::string& hostname, const int port )
-{
-    if( port < 0 || port > 65535 ) return NULL;
-    if( hostname.empty() ) return NULL;
-
-    int ret;
-    struct addrinfo hints, *res;
-    const int poststrlng = 256;
-    char port_str[ poststrlng ];
-    memset( &hints, 0, sizeof( addrinfo ) );
-    if( m_data.use_ipv6 ) hints.ai_family = AF_UNSPEC;
-    else hints.ai_family = AF_INET;
-    hints.ai_socktype = SOCK_STREAM;
-
-    snprintf( port_str, poststrlng, "%d", port );
-    ret = getaddrinfo( hostname.c_str(), port_str, &hints, &res );
-    if( ret ) {
-        MISC::ERRMSG( m_data.str_code );
-        return NULL;
-    }
-    
-#ifdef _DEBUG    
-    std::cout << "host = " << hostname
-              << " ipv6 = " << m_data.use_ipv6
-              << ", ip =" << inet_ntoa( (  ( sockaddr_in* )( res->ai_addr ) )->sin_addr ) << std::endl;
-#endif
-
-    return res;
-}
-
-
-
-//
 // 送信メッセージ作成
 //
 const std::string Loader::create_msg_send()
 {
     bool post_msg = ( !m_data.str_post.empty() && !m_data.head );
-    bool use_proxy = ( ! m_data.host_proxy.empty() );
+    bool use_proxy = ! m_data.host_proxy.empty() && ! m_data.use_ssl;
 
     std::ostringstream msg;
     msg.clear();
@@ -1097,8 +869,7 @@
     std::cout << "Loader::receive_header : read_size = " << read_size << std::endl;
 #endif
 
-    buf[ read_size ] = '\0';
-    m_data.str_header = buf;
+    m_data.str_header.assign( buf, read_size );
     size_t lng_header = m_data.str_header.find( "\r\n\r\n" );
     if( lng_header != std::string::npos ) lng_header += 4;
     else{
@@ -1119,11 +890,7 @@
                 
     // 残りのデータを前に移動
     read_size -= lng_header;
-    if( read_size ){
-
-        memmove( buf, buf+ lng_header, read_size );
-        buf[ read_size ] = '\0';
-    }
+    if( read_size ) memmove( buf, buf + lng_header, read_size );
 
     return HTTP_OK;
 }
@@ -1180,6 +947,14 @@
     // Content-Type
     m_data.contenttype = analyze_header_option( "Content-Type: " );
     
+    // ERROR
+    m_data.error = analyze_header_option( "ERROR: " );
+
+    // Thread-Status
+    m_data.threadstatus = 0;
+    str_tmp = analyze_header_option( "Thread-Status: " );
+    if( ! str_tmp.empty() ) m_data.threadstatus = atoi( str_tmp.c_str() );
+
     // chunked か
     m_use_chunk = false;
     str_tmp = analyze_header_option( "Transfer-Encoding: " );
@@ -1207,7 +982,9 @@
     for( ; it != m_data.list_cookies.end(); ++it ) std::cout << "cookie = " << (*it) << std::endl;
 
     std::cout << "location = " << m_data.location << std::endl;
-    std::cout << "contenttype = " << m_data.contenttype<< std::endl;            
+    std::cout << "contenttype = " << m_data.contenttype << std::endl;
+    std::cout << "error = " << m_data.error << std::endl;
+    std::cout << "threadstatus = " << m_data.threadstatus << std::endl;
     if( m_use_chunk ) std::cout << "m_use_chunk = true\n";
     if( m_use_zlib )  std::cout << "m_use_zlib = true\n";
 
@@ -1225,15 +1002,29 @@
 //
 std::string Loader::analyze_header_option( const std::string& option )
 {
-    size_t i = 0, i2 = 0;
-    i = m_data.str_header.find( option, 0 );    
-    if( i != std::string::npos ){
-        const size_t option_length = option.length();
-        i2 = m_data.str_header.find( "\r\n", i );
-        if( i2 == std::string::npos ) i2 = m_data.str_header.find( "\n", i );
-        if( i2 != std::string::npos ) return m_data.str_header.substr( i + option_length, i2 - ( i + option_length ) );
+    char accept[3] = { 0, 0, 0 };
+    const char ch = option[ 0 ];
+
+    if( ( ch >= 'A' && ch <= 'Z' ) || ( ch >= 'a' && ch <= 'z' ) ){
+        accept[ 0 ] = ch & ~0x20;
+        accept[ 1 ] = ch | 0x20;
+    }
+    else accept[ 0 ] = ch;
+
+    const char *p1, *p2 = m_data.str_header.c_str();
+
+    for( ; ( p1 = strpbrk( p2, accept ) ) != NULL ; p2 = p1 + 1 ){
+        if( strncasecmp( p1, option.c_str(), option.length() ) == 0 ) break;
     }
 
+    if( p1 != NULL ){
+        p2 = strstr( p1, "\r\n" );
+        if( p2 == NULL ) p2 = strchr( p1, '\n' );
+        if( p2 != NULL ){
+            p1 += option.length();
+            return std::string( p1, p2 - p1 );
+        }
+    }
     return std::string();
 }
 
@@ -1298,19 +1089,19 @@
 
                 // バッファオーバーフローのチェック
                 if( ( long )( m_pos_sizepart - m_str_sizepart ) >= 64 ){
-                    MISC::ERRMSG( "buffer over flow at skip_chunk" );
-                    return false;
+                    m_use_chunk = false;
+                    MISC::ERRMSG( "chunk specified but maybe no chunk data" );
+                    return true;
                 }
                 
-                *( m_pos_sizepart ) =  buf[ pos_chunk ];
+                *m_pos_sizepart =  buf[ pos_chunk ];
 
                 // \n が来たらデータ部のサイズを取得
                 if( buf[ pos_chunk ] == '\n' ){
 
                     ++pos_chunk;
                     
-                    *( m_pos_sizepart ) = '\0';
-                    if( *( m_pos_sizepart -1 ) == '\r' ) *( m_pos_sizepart -1 ) = '\0'; // "\r\n"の場合
+                    *m_pos_sizepart = '\0';
                     m_lng_leftdata = strtol( m_str_sizepart, NULL, 16 );
                     m_pos_sizepart = m_str_sizepart;
                     
@@ -1331,7 +1122,14 @@
 
             // データを前に詰める
             if( m_lng_leftdata ){
-                for( ; m_lng_leftdata > 0 && pos_chunk < read_size; --m_lng_leftdata, ++pos_chunk );
+                if( m_lng_leftdata < read_size - pos_chunk ){
+                    pos_chunk += m_lng_leftdata;
+                    m_lng_leftdata = 0;
+                }
+                else{
+                    m_lng_leftdata -= read_size - pos_chunk;
+                    pos_chunk = read_size;
+                }
                 size_t buf_size_tmp = pos_chunk - pos_data_chunk_start;
                 if( buf_size != pos_data_chunk_start && buf_size_tmp ) memmove( buf + buf_size , buf + pos_data_chunk_start,  buf_size_tmp );
                 buf_size +=  buf_size_tmp;
@@ -1342,7 +1140,7 @@
         }
 
         // データ部→サイズ部切り替え中( "\r" と "\n" の間でサーバからの入力が分かれる時がある)
-        if( m_status_chunk == 2 ){
+        if( m_status_chunk == 2 && pos_chunk < read_size ){
 
             unsigned char c = buf[ pos_chunk ];
             if( c != '\r' && c != '\n' ){
@@ -1351,8 +1149,12 @@
             }
 
             // \r\nが来たらサイズ部に戻る
-            if( c == '\r' ) ++pos_chunk; else break;      
-            if( buf[ pos_chunk ] == '\n' ) ++pos_chunk; else break;
+            if( c == '\r' && ++pos_chunk >= read_size ) break;
+            if( buf[ pos_chunk ] != '\n' ){
+                MISC::ERRMSG( "broken chunked data." );
+                return false;
+            }
+            ++pos_chunk;
             
 #ifdef _DEBUG_CHUNKED
             std::cout << "[[ skip_chunk : data chunk finished. ]]\n";
@@ -1364,7 +1166,6 @@
         if( pos_chunk == read_size ){
             
             read_size = buf_size;
-            buf[ read_size ] = '\0';
             
 #ifdef _DEBUG_CHUNKED
             std::cout << "[[ skip_chunk : output = " << read_size << " ]]\n\n";
@@ -1534,7 +1335,7 @@
 #endif    
 
     // zlibの入力バッファに値セット
-    if( m_zstream.avail_in + read_size > m_lng_buf_zlib_in ){ // オーバーフローのチェック
+    if( ( m_zstream.avail_in + read_size ) > m_lng_buf_zlib_in ){ // オーバーフローのチェック
 
         MISC::ERRMSG( "buffer over flow at zstream_in : " + m_data.url );
         return false;
@@ -1556,7 +1357,6 @@
         if( ret == Z_OK || ret == Z_STREAM_END ){
             
             byte_out = m_lng_buf_zlib_out - m_zstream.avail_out;
-            m_buf_zlib_out[ byte_out ] = '\0';
             m_data.size_data += byte_out;
             
 #ifdef _DEBUG
@@ -1578,7 +1378,7 @@
     } while ( byte_out );
 
     // 入力バッファに使ってないデータが残っていたら前に移動
-    if( m_zstream.avail_in ) memmove( m_buf_zlib_in, m_buf_zlib_in + ( read_size - m_zstream.avail_in ),  m_zstream.avail_in );
+    if( m_zstream.avail_in ) memmove( m_buf_zlib_in, m_buf_zlib_in + ( m_lng_buf_zlib_in - m_zstream.avail_in ),  m_zstream.avail_in );
 
     return true;
 }
@@ -1586,56 +1386,6 @@
 
 
 //
-// sent, recv待ち
-//
-const bool Loader::wait_recv_send( const int fd, const bool recv )
-{
-    if( !fd ) return true;
-
-    // 同期している場合は何もしない
-    if( !m_data.async ) return true;
-
-    int count = 0;
-    for(;;){
-
-        errno = 0;
-
-        int ret;
-        fd_set fdset;
-        FD_ZERO( &fdset );
-        FD_SET( fd , &fdset );
-
-        timeval timeout;
-        memset( &timeout, 0, sizeof( timeval ) );
-        timeout.tv_sec = 1;
-
-        if( recv ) ret = select( fd+1 , &fdset , NULL , NULL , &timeout );
-        else ret = select( fd+1 , NULL, &fdset , NULL , &timeout );
-
-#ifdef _DEBUG
-        if( errno == EINTR && ret < 0 ) std::cout << "Loader::wait_recv_send : errno = EINTR " << errno << std::endl;
-#endif
-        if( errno != EINTR && ret < 0 ){
-#ifdef _DEBUG
-            std::cout << "Loader::wait_recv_send : errno = " << errno << std::endl;
-#endif
-            MISC::ERRMSG( "select failed" );
-            break;
-        }
-
-        if( errno != EINTR && FD_ISSET( fd, &fdset ) ) return true;
-        if( m_stop ) break;
-        if( ++count >= m_data.timeout ) break;
-#ifdef _DEBUG
-        std::cout << "Loader::wait_recv_send ret = " << ret << " errno = " << errno << " timeout = " << count << std::endl;
-#endif
-    }
-    
-    return false;
-}
-
-
-//
 // ローディング終了処理
 //
 void Loader::finish_loading()
diff -aurNZ jd-2.8.9-150226/src/jdlib/loader.h jd-2.8.9-a171004/src/jdlib/loader.h
--- jd-2.8.9-150226/src/jdlib/loader.h	2014-01-26 00:08:56.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/loader.h	2016-04-15 15:07:49.626702000 +0900
@@ -16,14 +16,6 @@
 #include <list>
 #include <zlib.h>
 
-#ifdef _WIN32
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
-#include <netdb.h>
-#endif
-
 // zlibが1.2よりバージョンが低いか判定する
 #ifndef ZLIB_VERNUM
 #define ZLIB_VERNUM 0x1000
@@ -44,7 +36,6 @@
     class Loader
     {
         LOADERDATA m_data;
-        struct addrinfo* m_addrinfo;
 
         bool m_stop; // = true にするとスレッド停止
         bool m_loading;
@@ -101,9 +92,7 @@
 
         void clear();
         void run_main();
-        struct addrinfo* get_addrinfo( const std::string& hostname, const int port );
         const std::string create_msg_send();
-        const bool wait_recv_send( const int fd, const bool recv );
 
         // ローディング終了処理
         void finish_loading();
diff -aurNZ jd-2.8.9-150226/src/jdlib/loaderdata.cpp jd-2.8.9-a171004/src/jdlib/loaderdata.cpp
--- jd-2.8.9-150226/src/jdlib/loaderdata.cpp	2009-03-15 13:07:58.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/loaderdata.cpp	2016-05-10 03:28:04.303404000 +0900
@@ -31,11 +31,10 @@
     path.clear();
     port = 0;
     use_ssl = false;
-    async = true;
-    use_ipv6 = CONFIG::get_use_ipv6();
             
     str_post.clear();
 
+    protocol_proxy = 0;
     host_proxy.clear();
     port_proxy = 0;
     basicauth_proxy.clear();
@@ -56,6 +55,8 @@
     basicauth.clear();
     size_buf = 0;
     timeout = 0;
+    error.clear();
+    threadstatus = 0;
 }
 
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/loaderdata.h jd-2.8.9-a171004/src/jdlib/loaderdata.h
--- jd-2.8.9-150226/src/jdlib/loaderdata.h	2009-03-15 13:07:58.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/loaderdata.h	2016-05-10 03:27:00.227405000 +0900
@@ -26,22 +26,21 @@
         std::string protocol;
         std::string host;
         std::string path;
-        long port;
+        int port;
         bool use_ssl; // https
-        bool async; // 非同期ソケット使用
-        bool use_ipv6; // ipv6使用
 
         std::string str_post;
         
+        int protocol_proxy;
         std::string host_proxy;
-        long port_proxy;
+        int port_proxy;
         std::string basicauth_proxy; // proxy 認証
 
         std::string agent;
         std::string referer;
         std::string ex_field;  // 送信時にヘッダに追加するフィールド
         std::string str_header;
-        long code;
+        int code;
         std::string str_code;
         std::string date;
         std::string modified;
@@ -52,7 +51,9 @@
         std::string contenttype;
         std::string basicauth;
         size_t size_buf;
-        long timeout;
+        int timeout;
+        std::string error;
+        int threadstatus;
         
         LOADERDATA();
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/misccharcode.cpp jd-2.8.9-a171004/src/jdlib/misccharcode.cpp
--- jd-2.8.9-150226/src/jdlib/misccharcode.cpp	2008-02-08 23:41:57.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/misccharcode.cpp	2016-04-18 18:28:47.319875000 +0900
@@ -3,6 +3,7 @@
 //#define _DEBUG
 #include "jddebug.h"
 
+#include "jdiconv.h"
 #include "misccharcode.h"
 
 #include <cstring>
@@ -11,6 +12,27 @@
 // チェックする最大バイト数
 #define CHECK_LIMIT 1024
 
+
+static char const * const encoding_string[] = { "ISO-8859-1", "ASCII", "EUCJP-MS", "ISO2022-JP", "MS932", "UTF-8" };
+
+const char* MISC::charcode_to_cstr( const CharCode charcode )
+{
+    CharCode code = charcode;
+    if( code > CHARCODE_UTF8 || code < CHARCODE_UNKNOWN ) code = CHARCODE_UNKNOWN;
+    return encoding_string[ code ];
+}
+
+CharCode MISC::charcode_from_cstr( const char* encoding )
+{
+    CharCode code = CHARCODE_UNKNOWN;
+
+    for( size_t i = sizeof( encoding_string ) / sizeof( char * ) - 1; i > 0; --i ){
+        if( strcmp( encoding_string[ i ], encoding ) == 0 ){ code = CharCode( i ); break; }
+    }
+    return code;
+}
+
+
 /*--- 制御文字とASCII -----------------------------------------------*/
 
 // [ 制御文字 ] 0x00〜0x1F 0x7F
@@ -40,14 +62,13 @@
 // 2バイト目 0xA1〜0xFE
 #define EUC_RANGE_MULTI( target ) ( (unsigned char)( target - 0xA1 ) < 0x5E )
 //
-bool MISC::is_euc( const char* input, size_t& read_byte )
+bool MISC::is_eucjp( const char* input, const size_t length, size_t& read_byte )
 {
-    if( ! input ) return false;
+    if( ! input || ! length ) return false;
 
     size_t byte = read_byte;
-    const size_t input_length = strlen( input );
 
-    while( byte < input_length && byte < CHECK_LIMIT )
+    while( byte < length && byte < CHECK_LIMIT )
     {
         // 制御文字かアスキー
         if( CTRL_AND_ASCII_RANGE( input[ byte ] ) )
@@ -89,13 +110,11 @@
 // エスケープシーケンスの開始文字 ESC(0x1B)
 #define JIS_ESC_SEQ_START( target ) ( target == 0x1B )
 //
-bool MISC::is_jis( const char* input, size_t& byte )
+bool MISC::is_jis( const char* input, const size_t length, size_t& byte )
 {
-    if( ! input ) return false;
-
-    const size_t input_length = strlen( input );
+    if( ! input || ! length ) return false;
 
-    while( byte < input_length && byte < CHECK_LIMIT )
+    while( byte < length && byte < CHECK_LIMIT )
     {
         // ESCが出現したか否かだけで判断
         if( JIS_ESC_SEQ_START( input[ byte ] ) ) return true;
@@ -131,14 +150,13 @@
 // 0x80〜0xFC
 #define SJIS_RANGE_2_T( target ) ( (unsigned char)( target - 0x80 ) < 0x7D )
 //
-bool MISC::is_sjis( const char* input, size_t& read_byte )
+bool MISC::is_sjis( const char* input, const size_t length, size_t& read_byte )
 {
-    if( ! input ) return false;
+    if( ! input || ! length ) return false;
 
     size_t byte = read_byte;
-    const size_t input_length = strlen( input );
 
-    while( byte < input_length && byte < CHECK_LIMIT )
+    while( byte < length && byte < CHECK_LIMIT )
     {
         // 制御文字かアスキー
         if( CTRL_AND_ASCII_RANGE( input[ byte ] ) )
@@ -171,32 +189,33 @@
 //
 // 0xC0・0xC1はセキュリティ上の問題で使用が禁止されている
 //
-// [ 1バイト目の範囲 ] 0xC2〜0xFD [ RFC2279(破棄) ]
-// [ 1バイト目の範囲 ] 0xC2〜0xF4 [ RFC6329 ]
+// [ 1バイト目の範囲 ] 0xC2~0xFD [ RFC2279(破棄) ]
+// [ 1バイト目の範囲 ] 0xC2~0xF4 [ RFC6329 ]
 #define UTF_RANGE_1( target ) ( (unsigned char)( target - 0xC2 ) < 0x33 )
 //
-// [ 1バイト目 (2バイト文字) ] 先頭2ビットが1
-#define UTF_FLAG_2( target ) ( ( target & 0xC0 ) == 0xC0 )
+// [ 2バイト目以降 ] 0x80~0xBF 先頭ビットが10
+#define UTF_RANGE_MULTI_BYTE( target ) ( ( target & 0xC0 ) == 0x80 )
 //
-// [ 1バイト目 (3バイト文字) ] 先頭3ビットが1
-#define UTF_FLAG_3( target ) ( ( target & 0xE0 ) == 0xE0 )
+// [ 1バイト目 (2バイト文字) ] 先頭ビットが110
+//#define UTF_FLAG_2( target ) ( ( target & 0xE0 ) == 0xC0 )
+// [ 1バイト目 (2バイト文字) ] 0xC2~0xDF
+#define UTF_FLAG_2( target ) ( (unsigned char)( target - 0xC2 ) < 0x1E )
 //
-// [ 1バイト目 (4バイト文字) ] 先頭4ビットが1
-#define UTF_FLAG_4( target ) ( ( target & 0xF0 ) == 0xF0 )
+// [ 1バイト目 (3バイト文字) ] 先頭ビットが1110
+#define UTF_FLAG_3( target ) ( ( target & 0xF0 ) == 0xE0 )
 //
-// [ 2バイト目以降 ] 0x80〜0xBF
-#define UTF_RANGE_MULTI_BYTE( target ) ( (unsigned char)( target - 0x80 ) < 0x40 )
+// [ 1バイト目 (4バイト文字) ] 先頭ビットが11110
+#define UTF_FLAG_4( target ) ( ( target & 0xF8 ) == 0xF0 )
 //
-bool MISC::is_utf( const char* input, size_t& read_byte )
+bool MISC::is_utf8( const char* input, const size_t length, size_t& read_byte )
 {
-    if( ! input ) return false;
+    if( ! input || ! length ) return false;
 
     bool valid = true;
 
     size_t byte = read_byte;
-    const size_t input_length = strlen( input );
 
-    while( byte < input_length && byte < CHECK_LIMIT )
+    while( byte < length && byte < CHECK_LIMIT )
     {
         // 制御文字かアスキー
         if( CTRL_AND_ASCII_RANGE( input[ byte ] ) )
@@ -246,25 +265,237 @@
 // 各コードの判定でtrueの間は文字数分繰り返されるので
 // 速度の求められる繰り返し処理などで使わない事
 //
-int MISC::judge_char_code( const std::string& str )
+CharCode MISC::judge_char_code( const std::string& str )
 {
-    int code = CHARCODE_UNKNOWN;
+    CharCode code = CHARCODE_UNKNOWN;
 
     if( str.empty() ) return code;
 
     size_t read_byte = 0;
 
     // JISの判定
-    if( is_jis( str.c_str(), read_byte ) ) code = CHARCODE_JIS;
+    if( is_jis( str.c_str(), str.length(), read_byte ) ) code = CHARCODE_JIS;
     // JISの判定で最後まで進んでいたら制御文字かアスキー
     else if( read_byte == str.length() ) code = CHARCODE_ASCII;
     // UTF-8の範囲
-    else if( is_utf( str.c_str(), read_byte ) ) code = CHARCODE_UTF;
+    else if( is_utf8( str.c_str(), str.length(), read_byte ) ) code = CHARCODE_UTF8;
     // EUC-JPの範囲
-    else if( is_euc( str.c_str(), read_byte ) ) code = CHARCODE_EUC_JP;
+    else if( is_eucjp( str.c_str(), str.length(), read_byte ) ) code = CHARCODE_EUCJP;
     // Shift_JISの範囲
-    else if( is_sjis( str.c_str(), read_byte ) ) code = CHARCODE_SJIS;
+    else if( is_sjis( str.c_str(), str.length(), read_byte ) ) code = CHARCODE_SJIS;
+
+    return code;
+}
+
+
+//
+// utf-8 byte数
+//
+// 入力 : utfstr 入力文字 (UTF-8)
+// 戻り値 :  byte  長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を返す
+//
+int MISC::utf8bytes( const char* utfstr )
+{
+    int byte = 0;
+
+    if( utfstr && *utfstr != '\0' ){
+        char ch = ( unsigned char ) *utfstr;
+        if( ch >= 0 ) byte = 1;
+        else if( UTF_FLAG_3( ch ) ) byte = 3;
+        else if( UTF_FLAG_4( ch ) ) byte = 4;
+        else if( UTF_FLAG_2( ch ) ) byte = 2;
+#ifdef _DEBUG
+        else { // 不正なUTF8
+            std::cout << "MISC::utf8bytes : invalid 1st byte: char = " << (unsigned int) ch << std::endl;
+        }
+#endif
+    }
+
+    for( int i = 1; i < byte; ++i ){
+        if( ! UTF_RANGE_MULTI_BYTE( utfstr[ i ] ) ){
+#ifdef _DEBUG
+            // 不正なUTF8
+            std::cout << "MISC::utf8bytes : invalid code: char = " << (unsigned int) utf8str[ 0 ];
+            std::cout << ", " << (unsigned int) utf8str[ 1 ];
+            if( byte > 2 ) std::cout << ", " << (unsigned int) utf8str[ 2 ];
+            if( byte > 3 ) std::cout << ", " << (unsigned int) utf8str[ 3 ];
+            std::cout << std::endl;
+#endif
+            byte = 0;
+            break;
+        }
+    }
+
+    return byte;
+}
+
+
+//
+// utf-8 -> code point 変換
+//
+// 入力 : utfstr 入力文字 (UTF-8)
+// 出力 :  byte  長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を入れて返す
+// 戻り値 : code point
+//
+int MISC::utf8tocp( const char* utfstr, int& byte )
+{
+    int code = 0;
+    byte = utf8bytes( utfstr );
+
+    switch( byte ){
+    case 1:
+        code =  utfstr[ 0 ];
+        break;
+
+    case 2:
+        code = utfstr[ 0 ] & 0x1f;
+        code = ( code << 6 ) + ( utfstr[ 1 ] & 0x3f );
+        break;
+
+    case 3:
+        code = utfstr[ 0 ] & 0x0f;
+        code = ( code << 6 ) + ( utfstr[ 1 ] & 0x3f );
+        code = ( code << 6 ) + ( utfstr[ 2 ] & 0x3f );
+        break;
+
+    case 4:
+        code = utfstr[ 0 ] & 0x07;
+        code = ( code << 6 ) + ( utfstr[ 1 ] & 0x3f );
+        code = ( code << 6 ) + ( utfstr[ 2 ] & 0x3f );
+        code = ( code << 6 ) + ( utfstr[ 3 ] & 0x3f );
+        break;
+
+    default:
+        break;
+    }
 
     return code;
 }
 
+
+//
+// ucs2 -> utf8 変換
+//
+// 出力 : utfstr 変換後の文字
+// 戻り値 : バイト数
+//
+int MISC::cptoutf8( const int code,  char* utfstr )
+{
+    int byte = 0;
+
+    if( code <= 0x7f ){ // ascii
+        byte = 1;
+        utfstr[ 0 ] = code;
+    }
+
+    else if( code <= 0x07ff ){
+        byte = 2;
+        utfstr[ 0 ] = ( 0xc0 ) + ( code >> 6 );
+        utfstr[ 1 ] = ( 0x80 ) + ( code & 0x3f );
+    }
+
+    else if( code <= 0xffff){
+        byte = 3;
+        utfstr[ 0 ] = ( 0xe0 ) + ( code >> 12 );
+        utfstr[ 1 ] = ( 0x80 ) + ( ( code >>6 ) & 0x3f );
+        utfstr[ 2 ] = ( 0x80 ) + ( code & 0x3f );
+    }
+
+    else if( code <= 0x1fffff ){
+        byte = 4;
+        utfstr[ 0 ] = ( 0xf0 ) + ( code >> 18 );
+        utfstr[ 1 ] = ( 0x80 ) + ( ( code >>12 ) & 0x3f );
+        utfstr[ 2 ] = ( 0x80 ) + ( ( code >>6 ) & 0x3f );
+        utfstr[ 3 ] = ( 0x80 ) + ( code & 0x3f );
+    }
+
+#ifdef _DEBUG
+    else{
+        std::cout << "MISC::cptoutf8 : invalid code = " << code << std::endl;
+    }
+#endif
+
+    utfstr[ byte ] = 0;
+    return byte;
+}
+
+
+//
+// ucs の種類
+//
+int MISC::get_ucstype( const int code )
+{
+    if( code >= 0x0000 && code <= 0x007f ) return UCSTYPE_BASIC_LATIN;
+    if( code >= 0x3040 && code <= 0x309f ) return UCSTYPE_HIRA;
+    if( code >= 0x30a0 && code <= 0x30ff ) return UCSTYPE_KATA;
+
+    return UCSTYPE_OTHER;
+}
+
+//
+// WAVEDASHなどのWindows系UTF-8文字をUnix系文字と相互変換
+//
+std::string MISC::utf8_fix_wavedash( const std::string& str, const int mode )
+{
+    // WAVE DASH 問題
+    const size_t size = 4;
+    static const unsigned char Win[size][4] = {
+        { 0xef, 0xbd, 0x9e, '\0' }, // FULLWIDTH TILDE (U+FF5E)
+        { 0xe2, 0x80, 0x95, '\0' }, // HORIZONTAL BAR (U+2015)
+        { 0xe2, 0x88, 0xa5, '\0' }, // PARALLEL TO (U+2225)
+        { 0xef, 0xbc, 0x8d, '\0' }  // FULLWIDTH HYPHEN-MINUS (U+FF0D)
+    };
+    static const unsigned char Unix[size][4] = {
+        { 0xe3, 0x80, 0x9c, '\0' }, // WAVE DASH (U+301C)
+        { 0xe2, 0x80, 0x94, '\0' }, // EM DASH(U+2014)
+        { 0xe2, 0x80, 0x96, '\0' }, // DOUBLE VERTICAL LINE (U+2016)
+        { 0xe2, 0x88, 0x92, '\0' }  // MINUS SIGN (U+2212)
+    };
+
+    std::string ret(str);
+
+    if( mode == WINtoUNIX ){
+
+        for( size_t i = 0; i < ret.length(); i++ ) {
+            for( size_t s = 0; s < size; s++ ) {
+                if( ret[ i ] != (char)Win[ s ][ 0 ] || ret[ i+1 ] != (char)Win[ s ][ 1 ] || ret[ i+2 ] != (char)Win[ s ][ 2 ] )
+                    continue;
+                for( size_t t = 0; t < 3; t++ )
+                    ret[ i+t ] = (char)Unix[ s ][ t ];
+                i += 2;
+                break;
+            }
+        }
+
+    }else{
+
+        for( size_t i = 0; i < ret.length(); i++ ) {
+            for( size_t s = 0; s < size; s++ ) {
+                if( ret[ i ] != (char)Unix[ s ][ 0 ] || ret[ i+1 ] != (char)Unix[ s ][ 1 ] || ret[ i+2 ] != (char)Unix[ s ][ 2 ] )
+                    continue;
+                for( size_t t = 0; t < 3; t++ )
+                    ret[ i+t ] = (char)Win[ s ][ t ];
+                i += 2;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+
+//
+// 文字コードを from から to に変換
+//
+// 遅いので連続的な処理が必要な時は使わないこと
+//
+std::string MISC::Iconv( const std::string& str, const CharCode from, const CharCode to )
+{
+    if( from == to ) return str;
+
+    JDLIB::Iconv icv( from, to );
+    int byte_out;
+
+    return icv.convert( str.c_str(), str.length(), byte_out );
+}
diff -aurNZ jd-2.8.9-150226/src/jdlib/misccharcode.h jd-2.8.9-a171004/src/jdlib/misccharcode.h
--- jd-2.8.9-150226/src/jdlib/misccharcode.h	2008-01-08 00:48:14.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/misccharcode.h	2016-04-18 17:25:17.035933000 +0900
@@ -5,25 +5,64 @@
 #ifndef _MISCCHARCODE_H
 #define _MISCCHARCODE_H
 
+#include "charcode.h"
+
 #include <string>
 
+
 namespace MISC
 {
-    enum CodeSet
+    // get_ucstype()の戻り値
+    enum
+    {
+        UCSTYPE_BASIC_LATIN = 0,
+        UCSTYPE_HIRA,
+        UCSTYPE_KATA,
+
+        UCSTYPE_OTHER
+    };
+
+
+    // utf8_fix_wavedash のモード
+    enum
     {
-        CHARCODE_UNKNOWN = -1,
-        CHARCODE_ASCII = 0,
-        CHARCODE_EUC_JP,
-        CHARCODE_JIS,
-        CHARCODE_SJIS,
-        CHARCODE_UTF
+        UNIXtoWIN = 0,
+        WINtoUNIX
     };
 
-    bool is_euc( const char* input, size_t& read_byte );
-    bool is_jis( const char* input, size_t& read_byte );
-    bool is_sjis( const char* input, size_t& read_byte );
-    bool is_utf( const char* input, size_t& read_byte );
-    int judge_char_code( const std::string& str );
+
+    const char* charcode_to_cstr( const CharCode charcode );
+    CharCode charcode_from_cstr( const char* encoding );
+    bool is_eucjp( const char* input, const size_t length, size_t& read_byte );
+    bool is_jis( const char* input, const size_t length, size_t& read_byte );
+    bool is_sjis( const char* input, const size_t length, size_t& read_byte );
+    bool is_utf8( const char* input, const size_t length, size_t& read_byte );
+    CharCode judge_char_code( const std::string& str );
+
+    // utf-8 -> code point 変換
+    // 入力 : utfstr 入力文字 (UTF-8)
+    // 出力 :  byte  長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を入れて返す
+    // 戻り値 : unicode code point
+    int utf8tocp( const char* utfstr, int& byte );
+
+    // utf-8文字のbyte数
+    int utf8bytes( const char* utfstr );
+
+    // ucs の種類
+    int get_ucstype( const int code );
+
+    // code point -> utf8 変換
+    // 出力 : utfstr 変換後の文字
+    // 戻り値 : バイト数
+    int cptoutf8( const int code, char* utfstr );
+
+    // WAVEDASHなどのWindows系UTF-8文字をUnix系文字と相互変換
+    std::string utf8_fix_wavedash( const std::string& str, const int mode );
+
+    // 文字コードを from から to に変換
+    // 遅いので連続的な処理が必要な時は使わないこと
+    std::string Iconv( const std::string& str, const CharCode from, const CharCode to );
+
 }
 
 #endif
diff -aurNZ jd-2.8.9-150226/src/jdlib/miscgtk.cpp jd-2.8.9-a171004/src/jdlib/miscgtk.cpp
--- jd-2.8.9-150226/src/jdlib/miscgtk.cpp	2009-11-07 04:01:19.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/miscgtk.cpp	2015-06-23 14:23:00.542332000 +0900
@@ -4,6 +4,7 @@
 #include "jddebug.h"
 
 #include "miscgtk.h"
+#include "miscutil.h"
 #include "imgloader.h"
 
 
@@ -13,6 +14,195 @@
 };
 
 
+static struct color_map {
+    const char *name;
+    const char *rgb;
+} const color_names[] = {
+    // basic html colors
+    { "red",            "#ff0000" },
+    { "fuchsia",        "#ff00ff" },
+    { "purple",         "#800080" },
+    { "maroon",         "#800000" },
+    { "yellow",         "#ffff00" },
+    { "lime",           "#00ff00" },
+    { "green",          "#008000" },
+    { "olive",          "#808000" },
+    { "blue",           "#0000ff" },
+    { "aqua",           "#00ffff" },
+    { "teal",           "#008080" },
+    { "navy",           "#000080" },
+    { "white",          "#ffffff" },
+    { "silver",         "#c0c0c0" },
+    { "gray",           "#808080" },
+    { "black",          "#000000" },
+    { "orange",         "#ffa500" }, // since CSS2.1
+
+    // other X11 colors
+
+    //Red color names
+    { "indianred",      "#CD5C5C" },
+    { "lightcoral",     "#F08080" },
+    { "salmon",         "#FA8072" },
+    { "darksalmon",     "#E9967A" },
+    { "lightsalmon",    "#FFA07A" },
+    { "crimson",        "#DC143C" },
+//    { "red",            "#FF0000" },
+    { "firebrick",      "#B22222" },
+    { "darkred",        "#8B0000" },
+
+    //Pink color names
+    { "pink",           "#FFC0CB" },
+    { "lightpink",      "#FFB6C1" },
+    { "hotpink",        "#FF69B4" },
+    { "deeppink",       "#FF1493" },
+    { "mediumvioletred","#C71585" },
+    { "palevioletred",  "#DB7093" },
+
+    //Orange color names
+    { "lightsalmon",    "#FFA07A" },
+    { "coral",          "#FF7F50" },
+    { "tomato",         "#FF6347" },
+    { "orangered",      "#FF4500" },
+    { "darkorange",     "#FF8C00" },
+//    { "orange",         "#FFA500" },
+
+    //Yellow color names
+    { "gold",           "#FFD700" },
+//    { "yellow",         "#FFFF00" },
+    { "lightyellow",    "#FFFFE0" },
+    { "lemonchiffon",   "#FFFACD" },
+    { "lightgoldenrodyellow",   "#FAFAD2" },
+    { "papayawhip",     "#FFEFD5" },
+    { "moccasin",       "#FFE4B5" },
+    { "peachpuff",      "#FFDAB9" },
+    { "palegoldenrod",  "#EEE8AA" },
+    { "khaki",          "#F0E68C" },
+    { "darkkhaki",      "#BDB76B" },
+
+    //Purple color names
+    { "lavender",       "#E6E6FA" },
+    { "thistle",        "#D8BFD8" },
+    { "plum",           "#DDA0DD" },
+    { "violet",         "#EE82EE" },
+    { "orchid",         "#DA70D6" },
+//    { "fuchsia",        "#FF00FF" },
+    { "magenta",        "#FF00FF" },
+    { "mediumorchid",   "#BA55D3" },
+    { "mediumpurple",   "#9370DB" },
+    { "blueviolet",     "#8A2BE2" },
+    { "darkviolet",     "#9400D3" },
+    { "darkorchid",     "#9932CC" },
+    { "darkmagenta",    "#8B008B" },
+//    { "purple",         "#800080" },
+    { "indigo",         "#4B0082" },
+    { "slateblue",      "#6A5ACD" },
+    { "darkslateblue",  "#483D8B" },
+    { "mediumslateblue","#7B68EE" },
+
+    //Green color names
+    { "greenyellow",    "#ADFF2F" },
+    { "chartreuse",     "#7FFF00" },
+    { "lawngreen",      "#7CFC00" },
+//    { "lime",           "#00FF00" },
+    { "limegreen",      "#32CD32" },
+    { "palegreen",      "#98FB98" },
+    { "lightgreen",     "#90EE90" },
+    { "mediumspringgreen",  "#00FA9A" },
+    { "springgreen",    "#00FF7F" },
+    { "mediumseagreen", "#3CB371" },
+    { "seagreen",       "#2E8B57" },
+    { "forestgreen",    "#228B22" },
+//    { "green",          "#008000" },
+    { "darkgreen",      "#006400" },
+    { "yellowgreen",    "#9ACD32" },
+    { "olivedrab",      "#6B8E23" },
+//    { "olive",          "#808000" },
+    { "darkolivegreen", "#556B2F" },
+    { "mediumaquamarine",   "#66CDAA" },
+    { "darkseagreen",   "#8FBC8F" },
+    { "lightseagreen",  "#20B2AA" },
+    { "darkcyan",       "#008B8B" },
+//    { "teal",           "#008080" },
+
+    //Blue color names
+//    { "aqua",           "#00FFFF" },
+    { "cyan",           "#00FFFF" },
+    { "lightcyan",      "#E0FFFF" },
+    { "paleturquoise",  "#AFEEEE" },
+    { "aquamarine",     "#7FFFD4" },
+    { "turquoise",      "#40E0D0" },
+    { "mediumturquoise","#48D1CC" },
+    { "darkturquoise",  "#00CED1" },
+    { "cadetblue",      "#5F9EA0" },
+    { "steelblue",      "#4682B4" },
+    { "lightsteelblue", "#B0C4DE" },
+    { "powderblue",     "#B0E0E6" },
+    { "lightblue",      "#ADD8E6" },
+    { "skyblue",        "#87CEEB" },
+    { "lightskyblue",   "#87CEFA" },
+    { "deepskyblue",    "#00BFFF" },
+    { "dodgerblue",     "#1E90FF" },
+    { "cornflowerblue", "#6495ED" },
+    { "mediumslateblue","#7B68EE" },
+    { "royalblue",      "#4169E1" },
+//    { "blue",           "#0000FF" },
+    { "mediumblue",     "#0000CD" },
+    { "darkblue",       "#00008B" },
+//    { "navy",           "#000080" },
+    { "midnightblue",   "#191970" },
+
+    //Brown color names
+    { "cornsilk",       "#FFF8DC" },
+    { "blanchedalmond", "#FFEBCD" },
+    { "bisque",         "#FFE4C4" },
+    { "navajowhite",    "#FFDEAD" },
+    { "wheat",          "#F5DEB3" },
+    { "burlywood",      "#DEB887" },
+    { "tan",            "#D2B48C" },
+    { "rosybrown",      "#BC8F8F" },
+    { "sandybrown",     "#F4A460" },
+    { "goldenrod",      "#DAA520" },
+    { "darkgoldenrod",  "#B8860B" },
+    { "peru",           "#CD853F" },
+    { "chocolate",      "#D2691E" },
+    { "saddlebrown",    "#8B4513" },
+    { "sienna",         "#A0522D" },
+    { "brown",          "#A52A2A" },
+//    { "maroon",         "#800000" },
+
+    //White color names
+//    { "white",          "#FFFFFF" },
+    { "snow",           "#FFFAFA" },
+    { "honeydew",       "#F0FFF0" },
+    { "mintcream",      "#F5FFFA" },
+    { "azure",          "#F0FFFF" },
+    { "aliceblue",      "#F0F8FF" },
+    { "ghostwhite",     "#F8F8FF" },
+    { "whitesmoke",     "#F5F5F5" },
+    { "seashell",       "#FFF5EE" },
+    { "beige",          "#F5F5DC" },
+    { "oldlace",        "#FDF5E6" },
+    { "floralwhite",    "#FFFAF0" },
+    { "ivory",          "#FFFFF0" },
+    { "antiquewhite",   "#FAEBD7" },
+    { "linen",          "#FAF0E6" },
+    { "lavenderblush",  "#FFF0F5" },
+    { "mistyrose",      "#FFE4E1" },
+
+    //Grey color names
+    { "gainsboro",      "#DCDCDC" },
+    { "lightgrey",      "#D3D3D3" },
+//    { "silver",         "#C0C0C0" },
+    { "darkgray",       "#A9A9A9" },
+//    { "gray",           "#808080" },
+    { "dimgray",        "#696969" },
+    { "lightslategray", "#778899" },
+    { "slategray",      "#708090" },
+    { "darkslategray",  "#2F4F4F" },
+//    { "black",          "#000000" },
+};
+
+
 // Gdk::Color -> 16進数表記の文字列
 std::string MISC::color_to_str( const Gdk::Color& color )
 {
@@ -45,37 +235,28 @@
 // htmlカラー (#ffffffなど) -> 16進数表記の文字列
 std::string MISC::htmlcolor_to_str( const std::string& _htmlcolor )
 {
-    std::string htmlcolor = _htmlcolor;
+    std::string htmlcolor = MISC::tolower_str( _htmlcolor );
     int rgb[ 3 ];
 
-    if( htmlcolor == "red" )          htmlcolor = "#ff0000";
-    else if( htmlcolor == "fuchsia" ) htmlcolor = "#ff00ff";
-    else if( htmlcolor == "purple" )  htmlcolor = "#800080";
-    else if( htmlcolor == "maroon" )  htmlcolor = "#800000";
-    else if( htmlcolor == "yellow" )  htmlcolor = "#ffff00";
-    else if( htmlcolor == "lime" )    htmlcolor = "#00ff00";
-    else if( htmlcolor == "green" )   htmlcolor = "#008000";
-    else if( htmlcolor == "olive" )   htmlcolor = "#808000";
-    else if( htmlcolor == "blue" )    htmlcolor = "#0000ff";
-    else if( htmlcolor == "aqua" )    htmlcolor = "#00ffff";
-    else if( htmlcolor == "teal" )    htmlcolor = "#008080";
-    else if( htmlcolor == "navy" )    htmlcolor = "#000080";
-    else if( htmlcolor == "white" )   htmlcolor = "#ffffff";
-    else if( htmlcolor == "silver" )  htmlcolor = "#c0c0c0";
-    else if( htmlcolor == "gray" )    htmlcolor = "#808080";
-    else if( htmlcolor == "black" )   htmlcolor = "#000000";
-
-    int offset = 0;
-    if( htmlcolor.find( "#" ) == 0 ) offset = 1;
-
-    std::string tmpstr = htmlcolor.substr( offset, 2 );
-    rgb[ 0 ] = strtol( std::string( "0x" + tmpstr + tmpstr  ).c_str(), NULL, 16 );
+    if( htmlcolor[ 0 ] != '#' ){
+        const color_map* col = color_names;
+        const int lng_ary = sizeof( color_names ) / sizeof( color_map );
+        int i;
+        for( i = 0; i < lng_ary; ++i, ++col ){
+            if( htmlcolor[0] == *( col->name ) && htmlcolor == col->name ) break;
+        }
+        if( i >= lng_ary ) return std::string();
+        htmlcolor = col->rgb;
+    }
 
-    tmpstr = htmlcolor.substr( 2 + offset, 2 );
-    rgb[ 1 ] = strtol( std::string( "0x" + tmpstr + tmpstr  ).c_str(), NULL, 16 );
+    int n = ( htmlcolor.length() == 4 ) ? 1 : 2;
 
-    tmpstr = htmlcolor.substr( 4 + offset, 2 );
-    rgb[ 2 ] = strtol( std::string( "0x" + tmpstr + tmpstr  ).c_str(), NULL, 16 );
+    for( int i = 0; i < 3; ++i ){
+        const int offset = 1;
+        std::string tmpstr = htmlcolor.substr( offset + ( i * n ), n );
+        for( int j = 0; j < ( 3 - n ); ++j ) tmpstr += tmpstr;
+        rgb[ i ] = strtol( std::string( "0x" + tmpstr ).c_str(), NULL, 16 );
+    }
 
 #ifdef _DEBUG
     std::cout << "MISC::htmlcolor_to_gdkcolor color = " << htmlcolor 
@@ -127,7 +308,14 @@
 // gtk::entryの文字色を16進数表記の文字列で取得
 std::string MISC::get_entry_color_text()
 {
+    Gtk::Window win( Gtk::WINDOW_POPUP );
     Gtk::Entry entry;
+
+    win.add( entry );
+    win.move( 0,0 );
+    win.resize( 1,1 );
+    win.show_all();
+
     return color_to_str( entry.get_style()->get_text( Gtk::STATE_NORMAL ) );
 }
 
@@ -135,7 +323,14 @@
 // gtk::entryの背景色を16進数表記の文字列で取得
 std::string MISC::get_entry_color_base()
 {
+    Gtk::Window win( Gtk::WINDOW_POPUP );
     Gtk::Entry entry;
+
+    win.add( entry );
+    win.move( 0,0 );
+    win.resize( 1,1 );
+    win.show_all();
+
     return color_to_str( entry.get_style()->get_base( Gtk::STATE_NORMAL ) );
 }
 
diff -aurNZ jd-2.8.9-150226/src/jdlib/misctime.cpp jd-2.8.9-a171004/src/jdlib/misctime.cpp
--- jd-2.8.9-150226/src/jdlib/misctime.cpp	2012-03-03 00:45:58.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/misctime.cpp	2015-04-13 01:12:49.590950000 +0900
@@ -55,7 +55,7 @@
 //
 // 時刻を紀元からの経過秒に直す
 //
-const time_t MISC::datetotime( const std::string& date )
+const time_t MISC::datetotime( const std::string& date, const bool cookie_fmt )
 {
     if( date.empty() ) return 0;
 
@@ -68,7 +68,14 @@
     char monthes[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"  };
     char tzone[4];
-    rc = sscanf(date.c_str(), "%*3s, %2d %3s %4d %2d:%2d:%2d %3s",
+
+    const char* fmt;
+    const char* const datefmt = "%*3s, %2d %3s %4d %2d:%2d:%2d %3s";
+    const char* const datefmt_cookie = "%*3s, %2d-%3s-%4d %2d:%2d:%2d %3s";
+    if( cookie_fmt ) fmt = datefmt_cookie;
+    else fmt = datefmt;
+
+    rc = sscanf(date.c_str(), fmt,
                 &tm_out.tm_mday, month, &tm_out.tm_year,
                 &tm_out.tm_hour, &tm_out.tm_min, &tm_out.tm_sec, tzone);
     if ( rc != 7 )
@@ -102,8 +109,14 @@
 #ifdef _DEBUG
     std::cout << "locale = " << lcl << std::endl;
 #endif    
+    const char* fmt;
+    const char* const datefmt = "%a, %d %b %Y %T %Z";
+    const char* const datefmt_cookie = "%a, %d-%b-%Y %T %Z";
+    if( cookie_fmt ) fmt = datefmt_cookie;
+    else fmt = datefmt;
+
     if( ! lcl.empty() ) setlocale( LC_TIME, "C" ); 
-    char *ret = strptime( date.c_str(), "%a, %d %b %Y %T %Z", &tm_out );
+    char *ret = strptime( date.c_str(), fmt, &tm_out );
     if( ! lcl.empty() ) setlocale( LC_TIME, lcl.c_str() ); 
 
     if( ret == NULL ) return 0;
diff -aurNZ jd-2.8.9-150226/src/jdlib/misctime.h jd-2.8.9-a171004/src/jdlib/misctime.h
--- jd-2.8.9-150226/src/jdlib/misctime.h	2012-03-03 00:45:58.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/misctime.h	2015-04-13 20:08:41.557905000 +0900
@@ -33,7 +33,9 @@
 
     // 時刻の文字列を紀元からの経過秒に直す
     // (例) Tue, 27 Dec 2005 14:28:10 GMT -> 1135693690
-    const time_t datetotime( const std::string& date );
+    //   cookie_fmt が true の場合
+    //      Tue, 27-Dec-2005 14:28:10 GMT -> 1135693690
+    const time_t datetotime( const std::string& date, const bool cookie_fmt = false );
 
     // time_t を月日の文字列に変換
     // (例) mode == TIME_NORMAL なら 1135785252 -> 2005/12/29 0:54
diff -aurNZ jd-2.8.9-150226/src/jdlib/misctrip.cpp jd-2.8.9-a171004/src/jdlib/misctrip.cpp
--- jd-2.8.9-150226/src/jdlib/misctrip.cpp	2009-06-25 21:21:08.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/misctrip.cpp	2017-03-29 01:26:09.955710000 +0900
@@ -3,26 +3,41 @@
 //#define _DEBUG
 #include "jddebug.h"
 
+#include "misccharcode.h"
 #include "misctrip.h"
 #include "miscutil.h"
 
 #include <sstream>
 #include <cstring>
+#include <iomanip>
 #include <ctype.h>
-#include <unistd.h> // crypt
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
-#ifdef USE_OPENSSL
-#include <openssl/sha.h>
-#else // defined USE_GNUTLS
-#include <gcrypt.h>
+#if defined (USE_NSS)
+#  include <nss/pk11pub.h>
+#  include <nss/nss.h>
+#elif defined (USE_GNUTLS)
+#  include <gnutls/gnutls.h>
+#  include <gnutls/crypto.h>
+#elif defined (USE_OPENSSL)
+#  include <openssl/sha.h>
+#  include <openssl/hmac.h>
+#else // libgcrypt
+#  include <gcrypt.h>
 #endif
 
-#ifdef _WIN32
-#include <crypt.h>
+// for crypt
+#if defined (USE_OPENSSL)
+#  include <openssl/des.h>
+#elif defined (HAVE_CRYPT_H)
+#  include <crypt.h>
+#elif defined (_WIN32)
+#  include <encrypt.h>
+#else
+#  include <unistd.h>
 #endif
 
 
@@ -51,21 +66,26 @@
 {
     if( key.empty() ) return std::string();
 
-#ifdef USE_OPENSSL
-
-    const unsigned int digest_length = SHA_DIGEST_LENGTH;
+    unsigned int digest_length = 20; // SHA_DIGEST_LENGTH;
 
     unsigned char digest[ digest_length ];
 
-    // unsigned char *SHA1( const unsigned char *, size_t, unsigned char * );
-    SHA1( (const unsigned char *)key.c_str(), key.length(), digest );
+#if defined USE_NSS
+    PK11Context* context;
 
-#else // defined USE_GNUTLS
+    if( ( context = PK11_CreateDigestContext( SEC_OID_SHA1 ) ) ){
+        PK11_DigestOp( context, (const unsigned char*)key.c_str(), key.length() );
+        PK11_DigestFinal( context, digest, &digest_length, sizeof digest );
+        PK11_DestroyContext( context, PR_TRUE );
+    }
 
-    const unsigned int digest_length = gcry_md_get_algo_dlen( GCRY_MD_SHA1 );
+#elif defined USE_OPENSSL
+    SHA1( (const unsigned char *)key.c_str(), key.length(), digest );
 
-    unsigned char digest[ digest_length ];
+#elif defined USE_GNUTLS
+    gnutls_hash_fast( GNUTLS_DIG_SHA1, key.c_str(), key.length(), digest );
 
+#else // libgcrypt
     gcry_md_hash_buffer( GCRY_MD_SHA1, digest, key.c_str(), key.length() );
 
 #endif
@@ -152,7 +172,16 @@
                     strncat( salt, "..", 2 );
 
                     // crypt (key は先頭8文字しか使われない)
+#ifdef USE_OPENSSL
+                    char ret[14];
+                    const char *crypted = DES_fcrypt( key_binary, salt, ret );
+#elif defined( _GNU_SOURCE )
+                    struct crypt_data crydat;
+                    crydat.initialized = 0;
+                    const char *crypted = crypt_r( key_binary, salt, &crydat );
+#else
                     const char *crypted = crypt( key_binary, salt );
+#endif
 
                     // 末尾から10文字(cryptの戻り値はNULLでなければ必ず13文字)
                     if( crypted ) trip = std::string( crypted + 3 );
@@ -225,7 +254,16 @@
     salt.append( "H." );
 
     // crypt (key は先頭8文字しか使われない)
+#ifdef USE_OPENSSL
+    char ret[14];
+    const char *crypted = DES_fcrypt( key.c_str(), salt.c_str(), ret );
+#elif defined( _GNU_SOURCE )
+    struct crypt_data crydat;
+    crydat.initialized = 0;
+    const char *crypted = crypt_r( key.c_str(), salt.c_str(), &crydat );
+#else
     const char *crypted = crypt( key.c_str(), salt.c_str() );
+#endif
 
     std::string trip;
 
@@ -243,12 +281,12 @@
 // param2: 書き込む掲示板の文字コード
 // return: トリップ文字列
 /*--------------------------------------------------------------------*/
-const std::string MISC::get_trip( const std::string& str, const std::string& charset )
+std::string MISC::get_trip( const std::string& str, const CharCode charcode )
 {
     if( str.empty() ) return std::string();
 
-    // str の文字コードを UTF-8 から charset に変更して key に代入する
-    std::string key = MISC::Iconv( str, "UTF-8", charset );
+    // str の文字コードを UTF-8 から変更して key に代入する
+    std::string key = MISC::Iconv( str, CHARCODE_UTF8, charcode );
 
     std::string trip;
 
@@ -269,3 +307,78 @@
 
     return trip;
 }
+
+
+//
+// HMAC SHA256
+//
+std::string MISC::hmac_sha256(const std::string &key, const std::string &msg)
+{
+    unsigned char hash[32];
+    unsigned int len = sizeof( hash );
+
+#if defined USE_NSS
+    SECItem keyParams;
+    keyParams.type = siBuffer;
+    keyParams.data = (unsigned char *)key.c_str();
+    keyParams.len = key.length();
+
+    SECItem noParams;
+    noParams.type = siBuffer;
+    noParams.data = 0;
+    noParams.len = 0;
+
+    PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+    PK11SymKey *pk11key = PK11_ImportSymKey( slot, CKM_GENERIC_SECRET_KEY_GEN, PK11_OriginUnwrap, CKA_SIGN, &keyParams, 0 );
+    PK11Context *context = PK11_CreateContextBySymKey( CKM_SHA256_HMAC, CKA_SIGN, pk11key, &noParams);
+
+    if( context ){
+        PK11_DigestBegin( context );
+        PK11_DigestOp( context, (const unsigned char*)msg.c_str(), msg.length() );
+        PK11_DigestFinal( context, hash, &len, sizeof hash );
+
+        PK11_DestroyContext( context, PR_TRUE );
+    }
+    if( pk11key ) PK11_FreeSymKey( pk11key );
+    if( slot ) PK11_FreeSlot( slot );
+
+#elif defined USE_OPENSSL
+    HMAC_CTX hmac;
+    HMAC_CTX_init(&hmac);
+    HMAC_Init_ex(&hmac, &key[0], key.length(), EVP_sha256(), NULL);
+    HMAC_Update(&hmac, (unsigned char*)&msg[0], msg.length());
+    HMAC_Final(&hmac, hash, &len);
+    HMAC_CTX_cleanup(&hmac);
+
+#elif defined USE_GNUTLS
+    gnutls_hmac_fast( GNUTLS_MAC_SHA256, &key[0], key.length(), &msg[0], msg.length(), hash );
+
+#else
+# error Not Implemented
+#endif
+
+    std::ostringstream ss;
+    ss << std::hex << std::setfill('0');
+    for( unsigned int i = 0; i < len; i++ ){
+        ss << std::hex << std::setw(2) << (unsigned int)hash[i];
+    }
+
+    return ss.str();
+}
+
+//
+// FNV Hash Algorithm
+//
+uint32_t MISC::fnv_hash( const char *key, size_t length )
+{
+    const uint32_t OFFSET = 2166136261U;
+    const uint32_t PRIME = 16777619U;
+
+    uint32_t hash = OFFSET;
+    uint8_t *bytes = (uint8_t*)key;
+    for(size_t i = 0 ; i < length ; ++i) {
+        hash = (PRIME * hash) ^ bytes[i];
+    }
+
+    return hash;
+}
diff -aurNZ jd-2.8.9-150226/src/jdlib/misctrip.h jd-2.8.9-a171004/src/jdlib/misctrip.h
--- jd-2.8.9-150226/src/jdlib/misctrip.h	2009-06-24 22:35:07.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/misctrip.h	2017-03-28 22:50:26.463853000 +0900
@@ -5,12 +5,18 @@
 #ifndef _MISCTRIP_H
 #define _MISCTRIP_H
 
+#include "charcode.h"
+
 #include <string>
 
 namespace MISC
 {
     // トリップを取得 (SHA1等の新方式対応)
-    const std::string get_trip( const std::string& str, const std::string& charset );
+    std::string get_trip( const std::string& str, const CharCode charcode );
+    // HMAC SHA256
+    std::string hmac_sha256( const std::string &key, const std::string &msg );
+    // FNV Hash
+    uint32_t fnv_hash( const char *key, size_t length );
 };
 
 #endif
diff -aurNZ jd-2.8.9-150226/src/jdlib/miscutil.cpp jd-2.8.9-a171004/src/jdlib/miscutil.cpp
--- jd-2.8.9-150226/src/jdlib/miscutil.cpp	2014-04-27 20:09:24.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/miscutil.cpp	2017-03-22 19:04:18.680008000 +0900
@@ -3,6 +3,7 @@
 //#define _DEBUG
 #include "jddebug.h"
 
+#include "misccharcode.h"
 #include "miscutil.h"
 #include "miscmsg.h"
 #include "jdiconv.h"
@@ -12,6 +13,8 @@
 #include "dbtree/spchar_decoder.h"
 #include "dbtree/node.h"
 
+#include "cssmanager.h"
+
 #include <sstream>
 #include <cstring>
 #include <cstdio>
@@ -25,7 +28,7 @@
         
     std::list< std::string > lines;
     size_t i = 0, i2 = 0, r = 0;
-    while ( ( i2 = str.find( "\n", i ) ) != std::string::npos ){
+    while ( ( i2 = str.find_first_of( '\n', i ) ) != std::string::npos ){
         r = 0;
         if( str[ i2 - 1 ] == '\r' ) r = 1;
         if( i2 - i > 0 ){
@@ -111,8 +114,8 @@
 //
 const std::list< std::string > MISC::split_line( const std::string& str )
 {
-    std::string str_space = " ";
-    size_t lng_space = str_space.length();
+    const char* str_space = "\xE3\x80\x80"; // " " 空白
+    const size_t lng_space = 3;
     bool dquote;
 
     std::list< std::string > list_str;
@@ -129,7 +132,7 @@
             // 全角
             else if( str[ i ] == str_space[ 0 ] &&
                      str[ i +1 ] == str_space[ 1 ] &&
-                     ( lng_space == 2 || str[ i +2 ] == str_space[ 2 ] ) ) i += lng_space;
+                     str[ i +2 ] == str_space[ 2 ] ) i += lng_space;
 
             else break;
         }
@@ -157,7 +160,7 @@
                 // 全角
                 else if( str[ i2 ] == str_space[ 0 ] &&
                          str[ i2 +1 ] == str_space[ 1 ] &&
-                         ( lng_space == 2 || str[ i2 +2 ] == str_space[ 2 ] ) ){
+                         str[ i2 +2 ] == str_space[ 2 ] ){
                     lng_tmp = lng_space;
                     break;
                 }
@@ -294,13 +297,13 @@
 //
 const std::string MISC::remove_space( const std::string& str )
 {
-    std::string str_space = " ";
-    size_t lng_space = str_space.length();
+    const char* str_space = "\xE3\x80\x80"; // " " 空白
+    const size_t lng_space = 3;
 
     size_t lng = str.length();
     
     if( lng == 0 ) return str;
-    if( str.find( " " ) == std::string::npos ) return str;
+    if( str.find_first_of( ' ' ) == std::string::npos ) return str;
 
     // 前
     size_t i = 0;
@@ -312,7 +315,7 @@
         // 全角
         else if( str[ i ] == str_space[ 0 ] &&
                  str[ i +1 ] == str_space[ 1 ] &&
-                 ( lng_space == 2 || str[ i +2 ] == str_space[ 2 ] ) ) i += lng_space;
+                 str[ i +2 ] == str_space[ 2 ] ) i += lng_space;
         else break;
     }
 
@@ -326,7 +329,7 @@
         // 全角
         else if( str[ i2 - lng_space +1 ] == str_space[ 0 ] &&
                  str[ i2 - lng_space +2 ] == str_space[ 1 ] &&
-                 ( lng_space == 2 || str[ i2 - lng_space +3 ] == str_space[ 2 ] ) ) i2 -= lng_space;
+                 str[ i2 - lng_space +3 ] == str_space[ 2 ] ) i2 -= lng_space;
         else break;
     }
 
@@ -339,7 +342,7 @@
 //
 const std::string MISC::remove_spaces( const std::string& str )
 {
-    if( str.empty() ) return std::string();
+    if( str.empty() ) return str;
 
     size_t l = 0, r = str.length();
 
@@ -351,13 +354,15 @@
 
     // 最後の文字の位置は文字数より1少ない
     size_t p = r - 1;
-    while( p > 0
+    while( p > l
          && ( str[p] == '\n'
            || str[p] == '\r'
            || str[p] == '\t'
-           || str[p] == ' ' ) ){ --p; --r; }
+           || str[p] == ' ' ) ) --p;
+
+    if( l == 0 && p == r - 1 ) return str;
 
-    return str.substr( l, r - l );
+    return str.substr( l, p + 1 - l );
 }
 
 
@@ -446,6 +451,63 @@
     return str_out;
 }
 
+//
+// str1 を str2 に置き換え(ignore case)
+//
+const std::string MISC::replace_casestr( const std::string& str, const std::string& str1, const std::string& str2 )
+{
+    std::string str_out;
+
+//#ifndef _GNU_SOURCE
+#if 0
+    // 日本語ロケールだとラテン文字[a-zA-Z]以外はcase判定しないもよう
+    // コードは簡潔になるが遅くなるし旨味がない
+
+    const char *p0 = str.c_ctr(), *p1;
+
+    while( ( p1 = strcasestr( p0, str1.c_str() ) ) != NULL ){
+        str_out.append( p0, p1 - p0 );
+        str_out.append( str2 );
+        p0 = p1 + str1.length();
+    }
+#else
+    char accept[2] = { 0, 0 };
+
+    if( ( str1[ 0 ] >= 'A' && str1[ 0 ] <= 'Z' ) || ( str1[ 0 ] >= 'a' && str1[ 0 ] <= 'z' ) ){
+        accept[ 0 ] = str1[ 0 ] & ~0x20;
+        accept[ 1 ] = str1[ 0 ] | 0x20;
+    }
+    else accept[ 0 ] = str1[ 0 ];
+
+    const char *p0, *p1, *p2;
+    p0 = p1 = str.c_str();
+
+    struct searcheither{
+        inline static const char* fn( const char *p, const char* a ){
+            while( *p != a[ 0 ] && *p != a[ 1 ] && *p != '\0' ) ++p;
+            return ( *p == '\0' ) ? NULL : p;
+        }
+    };
+
+    while( ( p2 = searcheither::fn( p1, accept ) ) != NULL ){
+
+        if( strncasecmp( p2, str1.c_str(), str1.length() ) == 0 ){
+            str_out.append( p0, p2 - p0 );
+            str_out.append( str2 );
+            p0 = p1 = p2 + str1.length();
+            continue;
+        }
+        p1 = p2 + 1;
+    }
+#endif
+
+    if( p0 == str.c_str() ) return str;
+
+    str_out.append( str, p0 - str.c_str(), std::string::npos );
+
+    return str_out;
+}
+
 
 //
 // list_inから str1 を str2 に置き換えてリストを返す
@@ -455,7 +517,7 @@
 {
     std::list< std::string > list_out;
     std::list< std::string >::const_iterator it = list_in.begin();
-    for( ; it != list_in.end(); ++it ) list_out.push_back( replace_str( *it, str1, str2 ) );
+    for( ; it != list_in.end(); ++it ) list_out.push_back( MISC::replace_str( *it, str1, str2 ) );
     return list_out;
 }
 
@@ -488,24 +550,6 @@
 
 
 //
-// " を \" に置き換え
-//
-const std::string MISC::replace_quot( const std::string& str )
-{
-    return MISC::replace_str( str, "\"", "\\\"" );
-}
-
-
-//
-// \" を " に置き換え
-//
-const std::string MISC::recover_quot( const std::string& str )
-{
-    return MISC::replace_str( str, "\\\"", "\"" );
-}
-
-
-//
 // str 中に含まれている str2 の 数を返す
 //
 const int MISC::count_str( const std::string& str, const std::string& str2  )
@@ -592,10 +636,14 @@
 //
 const std::string MISC::itostr( const int n )
 {
+#if __cplusplus < 201103L
     std::ostringstream ss;
     ss << n;
 
     return ss.str();
+#else
+    return std::to_string( n );
+#endif
 }
 
 
@@ -683,7 +731,7 @@
     const size_t outstr_length = outstr.length();
 
     for( pos = 0, lng_str = 0; pos < outstr_length; pos += byte ){
-        MISC::utf8toucs2( outstr.c_str()+pos, byte );
+        byte = MISC::utf8bytes( outstr.c_str() + pos );
         if( byte > 1 ) lng_str += 2;
         else ++lng_str;
         if( lng_str >= maxsize ) break;
@@ -841,9 +889,9 @@
 //
 // HTMLエスケープ
 //
-// include_url : URL中でもエスケープする( デフォルト = true )
+// completely : URL中でもエスケープする( デフォルト = true )
 //
-const std::string MISC::html_escape( const std::string& str, const bool include_url )
+const std::string MISC::html_escape( const std::string& str, const bool completely )
 {
     if( str.empty() ) return str;
 
@@ -857,15 +905,15 @@
         char tmpchar = str.c_str()[ pos ];
 
         // URL中はエスケープしない場合
-        if( ! include_url )
+        if( ! completely )
         {
             // URLとして扱うかどうか
             // エスケープには影響がないので loose_url としておく
-            if( scheme != SCHEME_NONE ) is_url = is_url_char( str.c_str() + pos, true );
+            if( scheme != SCHEME_NONE ) is_url = MISC::is_url_char( str.c_str() + pos, true );
 
             // URLスキームが含まれているか判別
             int len = 0;
-            if( ! is_url ) scheme = is_url_scheme( str.c_str() + pos, &len );
+            if( ! is_url ) scheme = MISC::is_url_scheme( str.c_str() + pos, &len );
 
             // URLスキームが含まれていた場合は文字数分進めてループに戻る
             if( len > 0 )
@@ -876,17 +924,9 @@
             }
         }
 
-        // include_url = false でURL中ならエスケープしない
+        // completely = false でURL中ならエスケープしない
         if( is_url ) str_out += tmpchar;
-        else if( tmpchar == '&' )
-        {
-            const int bufsize = 64;
-            char out_char[ bufsize ];
-            int n_in, n_out;
-            const int type = DBTREE::decode_char( str.c_str() + pos, n_in, out_char, n_out, false );
-            if( type == DBTREE::NODE_NONE ) str_out += "&amp;";
-            else str_out += tmpchar;
-        }
+        else if( tmpchar == '&' ) str_out += "&amp;";
         else if( tmpchar == '\"' ) str_out += "&quot;";
         else if( tmpchar == '<' ) str_out += "&lt;";
         else if( tmpchar == '>' ) str_out += "&gt;";
@@ -895,7 +935,8 @@
 
 #ifdef _DEBUG
     if( str != str_out ){
-        std::cout << "MISC::html_escape\nstr = " << str << std::endl
+        std::cout << "MISC::html_escape" << std::endl
+                  << "str = " << str << std::endl
                   << "out = " << str_out << std::endl;
     }
 #endif
@@ -910,23 +951,24 @@
 const std::string MISC::html_unescape( const std::string& str )
 {
     if( str.empty() ) return str;
-    if( str.find( "&" ) == std::string::npos ) return str;
+    if( str.find_first_of( '&' ) == std::string::npos ) return str;
 
     std::string str_out;
-    const size_t str_length = str.length();
+    const char* pos = str.c_str();
+    const char* pos_end = pos + str.length();
 
-    for( size_t pos = 0; pos < str_length; ++pos ){
+    while( pos < pos_end ){
 
-        const int bufsize = 64;
-        char out_char[ bufsize ];
-        int n_in, n_out;
-        DBTREE::decode_char( str.c_str() + pos, n_in, out_char, n_out, false );
-
-        if( n_out ){
-            str_out += out_char;
-            pos += n_in -1;
-        }
-        else str_out += str.c_str()[ pos ];
+        // '&' までコピーする
+        while( *pos != '&' && *pos != '\0' ) str_out.push_back( *pos++ );
+        if( pos >= pos_end ) break;
+
+        // エスケープ用の文字参照をデコード
+        if( memcmp( pos , "&quot;", 6 ) == 0 ){ str_out.push_back( '"' ); pos += 6; }
+        else if( memcmp( pos, "&amp;", 5 ) == 0 ){ str_out.push_back( '&' ); pos += 5; }
+        else if( memcmp( pos, "&lt;", 4 ) == 0 ){ str_out.push_back( '<' ); pos += 4; }
+        else if( memcmp( pos, "&gt;", 4 ) == 0 ){ str_out.push_back( '>' ); pos += 4; }
+        else str_out.push_back( *pos++ );
     }
 
 #ifdef _DEBUG
@@ -942,6 +984,246 @@
 
 
 //
+// 特殊文字のデコード内部処理
+//
+// strは'&'で始まる文字列を指定すること
+// completely = true の時は'"' '&' '<' '>'も含めて変換する
+//
+static std::string spchar_impl( const char* str, int& n_in, const char pre_char, const bool completely )
+{
+    const int bufsize = 32;
+    char out_char[ bufsize ];
+    int n_out;
+    const int type = DBTREE::decode_char( str, n_in, out_char, n_out, false );
+
+    // 改行、タブ、スペースの処理
+    if( type == DBTREE::NODE_SP && pre_char != ' ' ){
+        out_char[ 0 ] = ' ';
+        n_out = 1;
+    }
+    // 変換できない文字
+    else if( type == DBTREE::NODE_NONE ){
+        out_char[ 0 ] = *str;
+        n_out = 1;
+        n_in = 1;
+    }
+    // エスケープする文字の場合は元に戻す
+    else if( ! completely && n_out == 1 ){
+        switch( *out_char ){
+            case '"':
+                out_char[ 0 ] = '&'; out_char[ 1 ] = 'q'; out_char[ 2 ] = 'u';
+                out_char[ 3 ] = 'o'; out_char[ 4 ] = 't'; out_char[ 5 ] = ';';
+                n_out = 6;
+                break;
+            case '&':
+                out_char[ 0 ] = '&'; out_char[ 1 ] = 'a'; out_char[ 2 ] = 'm';
+                out_char[ 3 ] = 'p'; out_char[ 4 ] = ';';
+                n_out = 5;
+                break;
+            case '<':
+                out_char[ 0 ] = '&'; out_char[ 1 ] = 'l'; out_char[ 2 ] = 't';
+                out_char[ 3 ] = ';';
+                n_out = 4;
+                break;
+            case '>':
+                out_char[ 0 ] = '&'; out_char[ 1 ] = 'g'; out_char[ 2 ] = 't';
+                out_char[ 3 ] = ';';
+                n_out = 4;
+                break;
+            default:
+                break;
+        }
+    }
+
+    return std::string( out_char, n_out );
+}
+
+
+//
+// HTMLをプレーンテキストに変換する
+//
+const std::string MISC::to_plain( const std::string& html )
+{
+    if( html.empty() ) return html;
+    if( html.find_first_of( '<' ) == std::string::npos
+            && html.find_first_of( '&' ) == std::string::npos ) return html;
+
+    std::string str_out;
+    const char* pos = html.c_str();
+    const char* pos_end = pos + html.length();
+
+    while( pos < pos_end ){
+
+        // '<' か '&' までコピーする
+        while( *pos != '<' && *pos != '&' && *pos != '\0' ) str_out.push_back( *pos++ );
+        if( pos >= pos_end ) break;
+
+        // タグを取り除く
+        if( *pos == '<' ){
+            while( *pos != '>' && *pos != '\0' ) pos++;
+            if( *pos == '>' ) ++pos;
+            continue;
+        }
+
+        // 文字参照を処理する
+        if( *pos == '&' ){
+            int n_in;
+            char pre = str_out.length() ? *( str_out.end() - 1 ) : 0;
+            str_out += spchar_impl( pos, n_in, pre, true );
+            pos += n_in;
+        }
+    }
+
+#ifdef _DEBUG
+    if( html != str_out )
+        std::cout << "MISC::to_plain" << std::endl
+                  << "html = " << html << std::endl
+                  << "plain = " << str_out << std::endl;
+#endif
+
+    return str_out;
+}
+
+
+//
+// HTMLをPango markupテキストに変換する
+//
+//
+const std::string MISC::to_markup( const std::string& html )
+{
+    if( html.empty() ) return html;
+    if( html.find_first_of( '<' ) == std::string::npos
+            && html.find_first_of( '&' ) == std::string::npos ) return html;
+
+    const char* pos = html.c_str();
+    const char* pos_end = pos + html.length();
+    std::string markuptxt;
+
+    while( pos < pos_end ){
+
+        // '<' か '&' までコピーする
+        while( *pos != '<' && *pos != '&' && *pos != '\0' ) markuptxt.push_back( *pos++ );
+        if( pos >= pos_end ) break;
+
+        // タグを処理する
+        if( *pos == '<' ){
+            ++pos;
+
+            // <mark>と<span>タグは色を変える
+            if( memcmp( pos, "mark", 4 ) == 0 || memcmp( pos, "span", 4 ) == 0 ){
+                std::string classname = ( ( *pos ) == 'm' ) ? "mark" : "";
+                pos += 4;
+                if( memcmp( pos, " class=\"", 8 ) == 0 ){
+                    pos += 8;
+                    const char* pos_name = pos;
+                    while( *pos != '"' && *pos != '\0' ) ++pos;
+                    classname = std::string( pos_name, pos - pos_name );
+                    if( *pos != '\0' ) ++pos;
+                }
+
+                markuptxt += "<span";
+
+                if( classname.size() ){
+                    CORE::Css_Manager* mgr = CORE::get_css_manager();
+                    int classid = mgr->get_classid( classname );
+                    if( classid != -1 ){
+                        CORE::CSS_PROPERTY css = mgr->get_property( classid );
+                        if( css.color != -1 ) markuptxt += " color=\"" + mgr->get_color( css.color ) + "\"";
+                        if( css.bg_color != -1 ) markuptxt += " background=\"" + mgr->get_color( css.bg_color ) + "\"";
+                    }
+                }
+
+                while( *pos != '>' && *pos != '\0' ) markuptxt.push_back( *pos++ );
+                markuptxt += ">";
+                if( *pos != '\0' ) ++pos;
+                continue;
+            }
+
+            // </mark> は </sapn>に置換する
+            if( memcmp( pos, "/mark>" , 6 ) == 0 || memcmp( pos, "/span>" , 6 ) == 0 ){
+                pos += 6;
+                markuptxt += "</span>";
+                continue;
+            }
+
+            // XXX その他のタグは取り除く
+            while( *pos != '>' && *pos != '\0' ) ++pos;
+            if( *pos == '>' ) ++pos;
+            continue;
+        }
+
+        // 文字参照を処理する
+        if( *pos == '&' ){
+            if( pos[ 1 ] == 'q' && pos[ 2 ] == 'u' && pos[ 3 ] == 'o' && pos[ 4 ] == 't' && pos[ 5 ] == ';' ){
+                markuptxt.push_back( '"' );
+                pos += 6;
+            }
+            else{
+                int n_in;
+                char pre = markuptxt.length() ? markuptxt[ markuptxt.length() - 1 ] : 0;
+                markuptxt += spchar_impl( pos, n_in, pre, false );
+                if( n_in == 1 && markuptxt[ markuptxt.length() - 1 ] == '&' ){
+                    markuptxt += "amp;";
+                }
+                pos += n_in;
+            }
+        }
+    }
+
+#ifdef _DEBUG
+    if( html != markuptxt )
+        std::cout << "MISC::to_markup" << std::endl
+                  << "html = " << html << std::endl
+                  << "markup = " << markuptxt << std::endl;
+#endif
+
+    return markuptxt;
+}
+
+
+//
+// HTMLの文字参照をデコード
+//
+// completely = true の時は'"' '&' '<' '>'もデコードする
+//
+const std::string MISC::chref_decode( const char* str, const int lng, const bool completely )
+{
+    std::string str_out;
+
+    if( lng <= 0 ) return str_out;
+    if( memchr( str, '&', lng ) == NULL ){
+        str_out.assign( str, lng );
+        return str_out;
+    }
+
+    const char *pos = str;
+    const char *pos_end = str + lng;
+
+    while( pos < pos_end ){
+
+        // '&' までコピーする
+        while( *pos != '&' && pos < pos_end ) str_out.push_back( *pos++ );
+        if( pos >= pos_end ) break;
+
+        // 文字参照のデコード
+        int n_in;
+        str_out += spchar_impl( pos, n_in, 0, completely );
+        pos += n_in;
+    }
+
+#ifdef _DEBUG
+    if( str != str_out ){
+        std::cout << "MISC::chref_decode" << std::endl
+                  << "str = " << str << std::endl
+                  << "out = " << str_out << std::endl;
+    }
+#endif
+
+    return str_out;
+}
+
+
+//
 // URL中のスキームを判別する
 //
 // 戻り値 : スキームタイプ
@@ -981,13 +1263,15 @@
         if( *( str_in + len ) == 's' ) ++len;
     }
     // sssp
-    // デフォルトでモザイクを解除するのでimg.2ch以外のアドレスにはリンクを張らない
-    else if( *str_in == 's' && *( str_in + 1 ) == 's' && *( str_in + 2 ) == 's' && *( str_in + 3 ) == 'p'
-             && *( str_in + 7 ) == 'i' && *( str_in + 8 ) == 'm' && *( str_in + 9 ) == 'g' && *( str_in + 10 ) == '.'
-             && *( str_in + 11 ) == '2' && *( str_in + 12 ) == 'c' && *( str_in + 13 ) == 'h'
-        )
-    {
-        scheme = SCHEME_SSSP;
+    else if( *str_in == 's' && *( str_in + 1 ) == 's' && *( str_in + 2 ) == 's' && *( str_in + 3 ) == 'p' ){
+        if( *( str_in + 7 ) == 'i' && *( str_in + 8 ) == 'm' && *( str_in + 9 ) == 'g' && *( str_in + 10 ) == '.'
+             && *( str_in + 11 ) == '2' && *( str_in + 12 ) == 'c' && *( str_in + 13 ) == 'h'){
+            scheme = SCHEME_SSSP;
+        }
+        else{
+            // XXX img.2ch以外のアドレスはHTTPスキームにする
+            scheme = SCHEME_HTTP;
+        }
         len = 4;
     }
 
@@ -1053,53 +1337,52 @@
 //
 // URLデコード
 //
-const std::string MISC::url_decode( const std::string& url )
+const std::string MISC::url_decode( const char* url, const size_t n )
 {
-    if( url.empty() ) return std::string();
+    std::string decoded;
+    if( n == 0 ) return decoded;
 
-    const size_t url_length = url.length();
-
-    char decoded[ url_length + 1 ];
-    memset( decoded, 0, sizeof( decoded ) );
-
-    unsigned int a, b;
-    for( a = 0, b = a; a < url_length; ++a, ++b )
+    unsigned int a;
+    for( a = 0; a < n; ++a )
     {
-        if( url[a] == '%' && ( a + 2 < url_length ) )
+        if( url[a] == '%' && ( a + 2 ) < n )
         {
             char src[3] = { url[ a + 1 ], url[ a + 2 ], '\0' };
             char tmp[3] = { '\0', '\0', '\0' };
 
             if( chrtobin( src, tmp ) == 2 )
             {
+                // 改行はLFにする
+                if( *tmp == '\n' && !decoded.empty() && *--decoded.end() == '\r' )
+                    decoded.erase( --decoded.end() );
                 // '%4A' など、2文字が変換できていること
-                decoded[b] = *tmp;
+                decoded.push_back( *tmp );
                 a += 2;
             }
             else
             {
                 // 変換失敗は、単なる '%' 文字として扱う
-                decoded[b] = url[a];
+                decoded.push_back( url[a] );
             }
         }
         else if( url[a] == '+' )
         {
-            decoded[b] = ' ';
+            decoded.push_back( ' ' );
         }
         else
         {
-            decoded[b] = url[a];
+            decoded.push_back( url[a] );
         }
     }
 
-    return std::string( decoded );
+    return decoded;
 }
 
 
 //
 // url エンコード
 //
-const std::string MISC::url_encode( const char* str, const size_t n )
+const std::string MISC::url_encode( const char* str, const size_t n, const CharCode coding )
 {
     if( str[ n ] != '\0' ){
         ERRMSG( "url_encode : invalid input." );
@@ -1107,62 +1390,76 @@
     }
 
     std::string str_encoded;
+    JDLIB::Iconv* icv = NULL;
+    if( coding != CHARCODE_UTF8 ) icv = new JDLIB::Iconv( CHARCODE_UTF8, coding );
     
-    for( size_t i = 0; i < n; i++ ){
+    size_t pos = 0;
+    while( pos < n ){
         
-        unsigned char c = str[ i ];
         const int tmplng = 16;
         char str_tmp[ tmplng ];
         
-        if( ! ( 'a' <= c && c <= 'z' ) &&
-            ! ( 'A' <= c && c <= 'Z' ) &&
-            ! ( '0' <= c && c <= '9' ) &&            
-            ( c != '*' ) &&
-            ( c != '-' ) &&
-            ( c != '.' ) &&
-            ( c != '@' ) &&
-            ( c != '_' )){
+        if( icv ){
+            size_t pos_start = pos;
+            while( str[ pos ] & 0x80 ) ++pos;
+            if( pos != pos_start ){
+                int byte_out;
+                const std::string str_enc = icv->convert( str + pos_start, pos - pos_start, byte_out );
+
+                // マルチバイト文字は全てパーセントエンコードする
+                for( int i = 0; i < byte_out; ++i ){
+                    unsigned char c = str_enc[ i ];
+                    snprintf( str_tmp, tmplng , "%%%02X", c );
+                    str_encoded += str_tmp;
+                }
 
-            snprintf( str_tmp, tmplng , "\%%%02x", c );
+                if( pos >= n ) break;
+            }
         }
-        else {
+
+        unsigned char c = str[ pos ];
+
+        // 非予約文字はそのまま
+        if( ( 'a' <= c && c <= 'z' ) || ( 'A' <= c && c <= 'Z' )
+            || ( '0' <= c && c <= '9' )
+            || ( c == '-' ) || ( c == '.' ) || ( c == '_' ) || ( c == '~' ) ){
             str_tmp[ 0 ] = c;
             str_tmp[ 1 ] = '\0';
         }
+        // スペースは'+'に置換
+        else if( c == ' ' ){
+            str_tmp[ 0 ] = '+';
+            str_tmp[ 1 ] = '\0';
+        }
+        // 改行を正規化
+        else if( c == '\n' ){
+            str_tmp[ 0 ] = '%'; str_tmp[ 1 ] = '0'; str_tmp[ 2 ] = 'D'; // CR ( %0d )
+            str_tmp[ 3 ] = '%'; str_tmp[ 4 ] = '0'; str_tmp[ 5 ] = 'A'; // LF ( %0a )
+            str_tmp[ 6 ] = '\0';
+        }
+        // CR は無視
+        else if( c == '\r' ){
+            str_tmp[ 0 ] = '\0';
+        }
+        // その他はパーセントエンコード
+        else{
+            snprintf( str_tmp, tmplng , "%%%02X", c );
+        }
 
         str_encoded += str_tmp;
+        ++pos;
     }
 
-    return str_encoded;
-}
-
+    if( icv ) delete icv;
 
-const std::string MISC::url_encode( const std::string& str )
-{
-    return url_encode( str.c_str(), str.length() );
-}
-
-
-//
-// 文字コード変換して url エンコード
-//
-// str は UTF-8 であること
-//
-const std::string MISC::charset_url_encode( const std::string& str, const std::string& charset )
-{
-    if( charset.empty() || charset == "UTF-8" ) return MISC::url_encode( str.c_str(), str.length() );
-
-    const std::string str_enc = MISC::Iconv( str, "UTF-8", charset );
-    return  MISC::url_encode( str_enc.c_str(), str_enc.length() );
+    return str_encoded;
 }
 
 
 //
-// 文字コード変換して url エンコード
+// 半角スペースまたは "" 単位で区切って url エンコード
 //
-// ただし半角スペースのところを+に置き換えて区切る
-//
-const std::string MISC::charset_url_encode_split( const std::string& str, const std::string& charset )
+const std::string MISC::url_encode_split( const std::string& str, const CharCode charcode )
 {
     std::list< std::string > list_str = MISC::split_line( str );
     std::list< std::string >::iterator it = list_str.begin();
@@ -1170,7 +1467,7 @@
     for( ; it != list_str.end(); ++it ){
 
         if( it != list_str.begin() ) str_out += "+";
-        str_out += MISC::charset_url_encode( *it, charset );
+        str_out += MISC::url_encode( it->c_str(), it->length(), charcode );
     }
 
     return str_out;
@@ -1224,31 +1521,6 @@
 
 
 
-
-//
-// 文字コードを coding_from から coding_to に変換
-//
-// 遅いので連続的な処理が必要な時は使わないこと
-//
-const std::string MISC::Iconv( const std::string& str, const std::string& coding_from, const std::string& coding_to )
-{
-    if( coding_from == coding_to ) return str;
-
-    char* str_bk = ( char* ) malloc( str.length() + 64 );
-    strcpy( str_bk, str.c_str() );
-
-    JDLIB::Iconv* libiconv = new JDLIB::Iconv( coding_from, coding_to );
-    int byte_out;
-
-    const std::string str_enc = libiconv->convert( str_bk, strlen( str_bk ), byte_out );
-
-    delete libiconv;
-    free( str_bk );
-
-    return str_enc;
-}
-
-
 //
 // 「&#数字;」形式の数字参照文字列の中の「数字」部分の文字列長
 //
@@ -1267,27 +1539,27 @@
     // offset == 2 なら 10 進数、3 なら16進数
     if( in_char[ offset ] == 'x' || in_char[ offset ] == 'X' ) ++offset;
 
-    // UCS2の2バイトの範囲でデコードするので最大65535
+    // UCS-4の範囲でデコードするので最大1114111
     // デコードするとき「;」で終端されていなくてもよい
 
     // デコード可能かチェック
     // 10 進数
     if( offset == 2 ){
 
-        // 最大5桁 (&#65536;)
-        for( lng = 0; lng <= 5; lng++ ){
+        // 最大7桁 (&#1114111;)
+        for( lng = 0; lng <= 7; lng++ ){
             if( in_char[ offset + lng ] < '0' || in_char[ offset + lng ] > '9' ) break;
         }
         
         // 桁数チェック
-        if( lng == 0 || lng == 6 ) return -1;
+        if( lng == 0 || lng == 8 ) return -1;
     }
 
     // 16 進数
     else{
 
-        // 最大4桁 (&#xFFFF;)
-        for( lng = 0; lng <= 4; lng++ ){
+        // 最大6桁 (&#x10FFFF;)
+        for( lng = 0; lng <= 6; lng++ ){
             if(
                 ! (
                     ( in_char[ offset + lng ] >= '0' && in_char[ offset + lng ] <= '9' )
@@ -1298,7 +1570,7 @@
         }
         
         // 桁数チェック
-        if( lng == 0 || lng == 5 ) return -1;
+        if( lng == 0 || lng == 7 ) return -1;
     }
 
     return lng;
@@ -1357,7 +1629,7 @@
             const int num = MISC::decode_spchar_number( str.c_str()+i, offset, lng );
 
             char out_char[ 64 ];
-            const int n_out = MISC::ucs2toutf8( num, out_char );
+            const int n_out = MISC::cptoutf8( num, out_char );
             if( ! n_out ){
                 str_out += str[ i ];
                 continue;
@@ -1374,168 +1646,6 @@
 
 
 //
-// utf-8 -> ucs2 変換
-//
-// 入力 : utfstr 入力文字 (UTF-8)
-//
-// 出力 :  byte  長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を入れて返す
-//
-// 戻り値 : ucs2
-//
-const int MISC::utf8toucs2( const char* utfstr, int& byte )
-{
-    int ucs2 = 0;
-    byte = 0;
-
-    if( utfstr[ 0 ] == '\0' ) return '\0';
-    
-    else if( ( ( unsigned char ) utfstr[ 0 ] & 0xf0 ) == 0xe0 ){
-        byte = 3;
-        ucs2 = utfstr[ 0 ] & 0x0f;
-        ucs2 = ( ucs2 << 6 ) + ( utfstr[ 1 ] & 0x3f );
-        ucs2 = ( ucs2 << 6 ) + ( utfstr[ 2 ] & 0x3f );        
-    }
-
-    else if( ( ( unsigned char ) utfstr[ 0 ] & 0x80 ) == 0 ){ // ascii
-        byte = 1;
-        ucs2 =  utfstr[ 0 ];
-    }
-
-    else if( ( ( unsigned char ) utfstr[ 0 ] & 0xe0 ) == 0xc0 ){
-        byte = 2;
-        ucs2 = utfstr[ 0 ] & 0x1f;
-        ucs2 = ( ucs2 << 6 ) + ( utfstr[ 1 ] & 0x3f );
-    }
-
-    else if( ( ( unsigned char ) utfstr[ 0 ] & 0xf8 ) == 0xf0 ){
-        byte = 4;
-        ucs2 = utfstr[ 0 ] & 0x07;
-        ucs2 = ( ucs2 << 6 ) + ( utfstr[ 1 ] & 0x3f );
-        ucs2 = ( ucs2 << 6 ) + ( utfstr[ 2 ] & 0x3f );
-        ucs2 = ( ucs2 << 6 ) + ( utfstr[ 3 ] & 0x3f );        
-    }
-
-    // 不正なUTF8
-    else {
-        byte = 1;
-        ucs2 =  utfstr[ 0 ];
-        ERRMSG( "MISC::utf8toucs2 : invalid code = " + MISC::itostr( ucs2 ) );
-    }
-
-    return ucs2;
-}
-
-
-
-
-//
-// ucs2 -> utf8 変換
-//
-// 出力 : utfstr 変換後の文字
-//
-// 戻り値 : バイト数
-//
-const int MISC::ucs2toutf8( const int ucs2,  char* utfstr )
-{
-    int byte = 0;
-
-    if( ucs2 < 0x7f ){ // ascii
-        byte = 1;
-        utfstr[ 0 ] = ucs2;
-    }
-
-    else if( ucs2 < 0x07ff ){
-        byte = 2;
-        utfstr[ 0 ] = ( 0xc0 ) + ( ucs2 >> 6 );
-        utfstr[ 1 ] = ( 0x80 ) + ( ucs2 & 0x3f );
-    }
-
-    else if( ucs2 < 0xffff){
-        byte = 3;
-        utfstr[ 0 ] = ( 0xe0 ) + ( ucs2 >> 12 );
-        utfstr[ 1 ] = ( 0x80 ) + ( ( ucs2 >>6 ) & 0x3f );
-        utfstr[ 2 ] = ( 0x80 ) + ( ucs2 & 0x3f );
-    }
-
-    else{
-        byte = 4;
-        utfstr[ 0 ] = ( 0xf0 ) + ( ucs2 >> 18 );
-        utfstr[ 1 ] = ( 0x80 ) + ( ( ucs2 >>12 ) & 0x3f );
-        utfstr[ 2 ] = ( 0x80 ) + ( ( ucs2 >>6 ) & 0x3f );
-        utfstr[ 3 ] = ( 0x80 ) + ( ucs2 & 0x3f );
-    }
-
-    utfstr[ byte ] = 0;
-    return byte;
-}
-
-
-//
-// ucs2 の種類
-//
-const int MISC::get_ucs2mode( const int ucs2 )
-{
-    if( ucs2 >= 0x0000 && ucs2 <= 0x007f ) return UCS2MODE_BASIC_LATIN;
-    if( ucs2 >= 0x3040 && ucs2 <= 0x309f ) return UCS2MODE_HIRA;
-    if( ucs2 >= 0x30a0 && ucs2 <= 0x30ff ) return UCS2MODE_KATA;
-
-    return UCS2MODE_OTHER;
-}
-
-//
-// WAVEDASHなどのWindows系UTF-8文字をUnix系文字と相互変換
-//
-const std::string MISC::utf8_fix_wavedash( const std::string& str, const int mode )
-{
-    // WAVE DASH 問題
-    const size_t size = 4;
-    const unsigned char Win[size][4] = {
-        { 0xef, 0xbd, 0x9e, '\0' }, // FULLWIDTH TILDE (U+FF5E)
-        { 0xe2, 0x80, 0x95, '\0' }, // HORIZONTAL BAR (U+2015)
-        { 0xe2, 0x88, 0xa5, '\0' }, // PARALLEL TO (U+2225)
-        { 0xef, 0xbc, 0x8d, '\0' }  // FULLWIDTH HYPHEN-MINUS (U+FF0D)
-    };
-    const unsigned char Unix[size][4] = {
-        { 0xe3, 0x80, 0x9c, '\0' }, // WAVE DASH (U+301C)
-        { 0xe2, 0x80, 0x94, '\0' }, // EM DASH(U+2014)
-        { 0xe2, 0x80, 0x96, '\0' }, // DOUBLE VERTICAL LINE (U+2016)
-        { 0xe2, 0x88, 0x92, '\0' }  // MINUS SIGN (U+2212)
-    };
-    
-    std::string ret(str);
-
-    if( mode == WINtoUNIX ){
-
-        for( size_t i = 0; i < ret.length(); i++ ) {
-            for( size_t s = 0; s < size; s++ ) {
-                if( ret[ i ] != (char)Win[ s ][ 0 ] || ret[ i+1 ] != (char)Win[ s ][ 1 ] || ret[ i+2 ] != (char)Win[ s ][ 2 ] )
-                    continue;
-                for( size_t t = 0; t < 3; t++ )
-                    ret[ i+t ] = (char)Unix[ s ][ t ];
-                i += 2;
-                break;
-            }
-        }
-
-    }else{
-   
-        for( size_t i = 0; i < ret.length(); i++ ) {
-            for( size_t s = 0; s < size; s++ ) {
-                if( ret[ i ] != (char)Unix[ s ][ 0 ] || ret[ i+1 ] != (char)Unix[ s ][ 1 ] || ret[ i+2 ] != (char)Unix[ s ][ 2 ] )
-                    continue;
-                for( size_t t = 0; t < 3; t++ )
-                    ret[ i+t ] = (char)Win[ s ][ t ];
-                i += 2;
-                break;
-            }
-        }
-    }
-
-    return ret;
-}
-
-
-//
 // str を大文字化
 //
 const std::string MISC::toupper_str( const std::string& str )
@@ -1640,15 +1750,13 @@
 {
     if( ! name || ! getenv( name ) ) return std::string();
 
-    char env[ size + 1 ];
-    env[ size ] = '\0';
-
-    strncpy( env, getenv( name ), size );
+    std::string env( getenv( name ) );
+    if( env.length() > size ) env.resize( size );
 
 #ifdef _WIN32
-    return recover_path( Glib::locale_to_utf8( std::string( env ) ) );
+    return recover_path( Glib::locale_to_utf8( env ) );
 #else
-    return std::string( env );
+    return env;
 #endif
 }
 
@@ -1740,7 +1848,7 @@
 //
 void MISC::asc( const char* str1, char* str2, int* table_pos, const size_t n )
 {
-    const size_t mrg = 18;
+    const size_t mrg = 4;
     size_t pos = 0;
     size_t pos2 = 0;
 
@@ -1758,8 +1866,8 @@
                 //  全角数字
                 if( 0x90 <= in3 && in3 <= 0x99 ){
 
-                    str2[ pos2 ] = '0' + in3 - 0x90;;
-                    table_pos[ pos2 ] = pos;
+                    str2[ pos2 ] = '0' + in3 - 0x90;
+                    if( table_pos ) table_pos[ pos2 ] = pos;
                     pos += 3;
                     ++pos2;
                     continue;
@@ -1769,7 +1877,7 @@
                 else if( 0xa1 <= in3 && in3 <= 0xba ){
 
                     str2[ pos2 ] = 'A' + in3 - 0xa1;
-                    table_pos[ pos2 ] = pos;
+                    if( table_pos ) table_pos[ pos2 ] = pos;
 
                     pos += 3;
                     ++pos2;
@@ -1781,7 +1889,7 @@
             else if( in2 == 0xbd && ( 0x81 <= in3 && in3 <= 0x9a ) ){
 
                 str2[ pos2 ] = 'a' + in3 - 0x81;
-                table_pos[ pos2 ] = pos;
+                if( table_pos ) table_pos[ pos2 ] = pos;
 
                 pos += 3;
                 ++pos2;
@@ -1816,19 +1924,24 @@
                     }
                 }
 
-                while( !flag_hkana && hkana_table1[ i ][ 0 ][ 0 ] != '\0' ){
+                while( hkana_table1[ i ][ 0 ][ 0 ] != '\0' ){
 
                     if( in == hkana_table1[ i ][ 0 ][ 0 ] && in2 == hkana_table1[ i ][ 0 ][ 1 ] && in3 == hkana_table1[ i ][ 0 ][ 2 ] ){
 
                         str2[ pos2 ] = hkana_table1[ i ][ 1 ][ 0 ];
                         str2[ pos2 +1 ] = hkana_table1[ i ][ 1 ][ 1 ];
                         str2[ pos2 +2 ] = hkana_table1[ i ][ 1 ][ 2 ];
-                        table_pos[ pos2 ] = pos;
+                        if( table_pos ){
+                            table_pos[ pos2 ] = pos;
+                            table_pos[ pos2 + 1 ] = -1;
+                            table_pos[ pos2 + 2 ] = -1;
+                        }
 
                         pos += 3;
                         if( dakuten ) pos += 3;
                         pos2 += 3;
                         flag_hkana = true;
+                        break;
                     }
                     ++i;
                 }
@@ -1837,7 +1950,7 @@
         }
 
         str2[ pos2 ] = str1[ pos ];
-        table_pos[ pos2 ] = pos;
+        if( table_pos ) table_pos[ pos2 ] = pos;
 
         ++pos;
         ++pos2;
@@ -1845,9 +1958,69 @@
 
     if( pos2 >= ( n - mrg ) ){
         ERRMSG( "MISC::asc : buffer overflow." );
-        pos2 = ( n - mrg ) - 1;
+        if( pos2 >= n ) pos2 = n - 1;
+    }
+
+    if( table_pos ) table_pos[ pos2 ] = pos;
+    str2[ pos2 ] = '\0';
+}
+
+
+//
+// UTF8文字列の正規化
+//
+// str1 : 変換する文字列
+// str2 : 出力先
+// table_pos : 置き換えた文字列の位置
+// n : str2 と table_pos のバッファサイズ
+//
+void MISC::norm( const char* str1, char* str2, int* table_pos, const size_t n )
+{
+    size_t pos1 = 0, pos2 = 0;
+    Glib::ustring ustr;
+    std::string str_norm;
+    int lng;
+
+    while( str1[ pos1 ] != '\0' ){
+
+        int nbytes = MISC::utf8bytes( str1 + pos1 );
+        if( nbytes <= 1 ){
+            str2[ pos2 ] = str1[ pos1 ];
+            if( table_pos ) table_pos[ pos2 ] = pos1;
+            pos1++;
+            pos2++;
+            continue;
+        }
+
+        // 異字体は纏める
+        int next = MISC::utf8tocp( str1 + pos1 + nbytes, lng );
+        int nchars = 1;
+        if( ( next >= 0x180b && next <= 0x180d ) ||
+            ( next >= 0xfe00 && next <= 0xfe0f ) ||
+            ( next >= 0xe0100 && next <= 0xe01ef ) ){
+            nchars++;
+            nbytes += lng;
+        }
+
+        ustr.assign( str1 + pos1, nchars );
+        str_norm = ustr.normalize( Glib::NORMALIZE_NFKD );
+
+        lng = str_norm.length();
+        if( pos2 + lng >= n - 1 ){
+            ERRMSG( "MISC::asc : buffer overflow." );
+            break;
+        }
+
+        str_norm.copy( str2 + pos2, lng );
+        if( table_pos ){
+            table_pos[ pos2 ] = pos1;
+            for( int i = 1; i < lng; ++i ) table_pos[ pos2 + i ] = -1;
+        }
+
+        pos1 += nbytes;
+        pos2 += lng;
     }
 
-    table_pos[ pos2 ] = pos;
+    if( table_pos ) table_pos[ pos2 ] = pos1;
     str2[ pos2 ] = '\0';
 }
diff -aurNZ jd-2.8.9-150226/src/jdlib/miscutil.h jd-2.8.9-a171004/src/jdlib/miscutil.h
--- jd-2.8.9-150226/src/jdlib/miscutil.h	2013-01-14 04:09:38.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/miscutil.h	2017-03-21 18:29:49.477364000 +0900
@@ -5,6 +5,8 @@
 #ifndef _MISCUTIL_H
 #define _MISCUTIL_H
 
+#include "charcode.h"
+
 #include <string>
 #include <cstring>
 #include <list>
@@ -25,24 +27,6 @@
                 SCHEME_SSSP
 	};
 
-     // get_ucs2mode()の戻り値
-     enum
-     {
-         UCS2MODE_BASIC_LATIN = 0,
-         UCS2MODE_HIRA,
-         UCS2MODE_KATA,
-
-         UCS2MODE_OTHER
-     };
-
-
-     // utf8_fix_wavedash のモード
-     enum
-     {
-         UNIXtoWIN = 0,
-         WINtoUNIX
-     };
-
     // str を "\n" ごとに区切ってlistにして出力
     const std::list< std::string > get_lines( const std::string& str );
 
@@ -95,6 +79,9 @@
     // str1 を str2 に置き換え
     const std::string replace_str( const std::string& str, const std::string& str1, const std::string& str2 );
 
+    // str1 を str2 に置き換え( ignore case 版 )
+    const std::string replace_casestr( const std::string& str, const std::string& str1, const std::string& str2 );
+
     // list_inから str1 を str2 に置き換えてリストを返す
     const std::list< std::string > replace_str_list( const std::list< std::string >& list_in, const std::string& str1, const std::string& str2 );
 
@@ -102,10 +89,10 @@
     const std::string replace_newlines_to_str( const std::string& str_in, const std::string& replace );
 
     // " を \" に置き換え
-    const std::string replace_quot( const std::string& str );
+    inline const std::string replace_quot( const std::string& str ){ return MISC::replace_str( str, "\"", "\\\"" ); }
 
     // \" を " に置き換え
-    const std::string recover_quot( const std::string& str );
+    inline const std::string recover_quot( const std::string& str ){ return MISC::replace_str( str, "\\\"", "\"" ); }
 
     // str 中に含まれている str2 の 数を返す
     const int count_str( const std::string& str, const std::string& str2 );
@@ -148,12 +135,22 @@
     const std::string regex_unescape( const std::string& str );
 
     // HTMLエスケープ
-    // include_url : URL中でもエスケープする( デフォルト = true )
-    const std::string html_escape( const std::string& str, const bool include_url = true );
+    // completely : URL中でもエスケープする( デフォルト = true )
+    const std::string html_escape( const std::string& str, const bool completely = true );
 
     // HTMLアンエスケープ
     const std::string html_unescape( const std::string& str );
 
+    // HTMLをプレーンテキストに変換する
+    const std::string to_plain( const std::string& html );
+
+    // HTMLをPango Markupテキストに変換する
+    const std::string to_markup( const std::string& html );
+
+    // HTML文字参照をデコード( completely=trueの場合は '&' '<' '>' '"' を含める )
+    const std::string chref_decode( const char* str, const int lng, const bool completely = true );
+    inline const std::string chref_decode( const std::string& str, const bool completely = true ){ return MISC::chref_decode( str.c_str(), str.length(), completely ); }
+
     // URL中のスキームを判別する
     // 戻り値 : スキームタイプ
     // length    : "http://"等の文字数
@@ -169,27 +166,19 @@
     const bool is_url_char( const char* str_in, const bool loose_url );
 
     // URLデコード
-    const std::string url_decode( const std::string& url );
+    const std::string url_decode( const char* url, const size_t n );
+    inline const std::string url_decode( const std::string& url ){ return url_decode( url.c_str(), url.length() ); }
 
     // urlエンコード
-    const std::string url_encode( const char* str, const size_t n );
-    const std::string url_encode( const std::string& str );
+    const std::string url_encode( const char* str, const size_t n, const CharCode charcode = CHARCODE_UTF8 );
+    inline const std::string url_encode( const std::string& str, const CharCode charcode = CHARCODE_UTF8 ){ return url_encode( str.c_str(), str.length(), charcode ); }
 
-    // 文字コードを変換して url エンコード
-    // str は UTF-8 であること
-    const std::string charset_url_encode( const std::string& str, const std::string& charset );
-
-    // 文字コード変換して url エンコード
-    // ただし半角スペースのところを+に置き換えて区切る
-    const std::string charset_url_encode_split( const std::string& str, const std::string& charset );
+    // 半角スペースまたは "" 単位で区切って url エンコード
+    const std::string url_encode_split( const std::string& str, const CharCode charcode );
 
     // BASE64
     const std::string base64( const std::string& str );
 
-    // 文字コードを coding_from から coding_to に変換
-    // 遅いので連続的な処理が必要な時は使わないこと
-    const std::string Iconv( const std::string& str, const std::string& coding_from, const std::string& coding_to );
-
     // 「&#数字;」形式の数字参照文字列の中の「数字」部分の文字列長
     //
     // in_char: 入力文字列、in_char[0] == "&" && in_char[1] == "#" であること
@@ -216,23 +205,6 @@
     // str に含まれる「&#数字;」形式の数字参照文字列を全てユニーコード文字に変換する
     const std::string decode_spchar_number( const std::string& str );
 
-    // utf-8 -> ucs2 変換
-    // 入力 : utfstr 入力文字 (UTF-8)
-    // 出力 :  byte  長さ(バイト) utfstr が ascii なら 1, UTF-8 なら 2 or 3 or 4 を入れて返す
-    // 戻り値 : ucs2
-    const int utf8toucs2( const char* utfstr, int& byte );
-
-    // ucs2 の種類
-    const int get_ucs2mode( const int ucs2 );
-
-    // ucs2 -> utf8 変換
-    // 出力 : utfstr 変換後の文字
-    // 戻り値 : バイト数
-    const int ucs2toutf8( const int ucs2, char* utfstr );
-
-    // WAVEDASHなどのWindows系UTF-8文字をUnix系文字と相互変換
-    const std::string utf8_fix_wavedash( const std::string& str, const int mode );
-
     // str を大文字化
     const std::string toupper_str( const std::string& str );
 
@@ -267,6 +239,11 @@
     // n : str2 と table_pos のバッファサイズ
     void asc( const char* str1, char* str2, int* table_pos, const size_t n );
 
+    // UTF-8文字列(str1) -> 正規化文字列(str2)
+    // table_pos : 置き換えた文字列の位置
+    // n : str2 と table_pos のバッファサイズ
+    void norm( const char* str1, char* str2, int* table_pos, const size_t n );
+
 
     // URL中のスキームを判別する
     inline const int is_url_scheme( const char* str_in, int* length )
diff -aurNZ jd-2.8.9-150226/src/jdlib/refptr_lock.h jd-2.8.9-a171004/src/jdlib/refptr_lock.h
--- jd-2.8.9-150226/src/jdlib/refptr_lock.h	2006-12-14 22:47:07.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/refptr_lock.h	2016-03-15 22:08:42.806689000 +0900
@@ -33,6 +33,7 @@
 
 
         T* operator -> (){ return m_p; }
+        const T* operator -> () const { return m_p; }
         bool operator == ( const T *p ) const { return( m_p == p ); }
         bool operator != ( const T *p ) const { return( m_p != p ); }
         bool operator ! () const { return ( m_p == NULL ); }
diff -aurNZ jd-2.8.9-150226/src/jdlib/ssl.cpp jd-2.8.9-a171004/src/jdlib/ssl.cpp
--- jd-2.8.9-150226/src/jdlib/ssl.cpp	2012-02-04 21:07:34.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/ssl.cpp	1970-01-01 09:00:00.000000000 +0900
@@ -1,259 +0,0 @@
-// ライセンス: GPL2
-
-//#define _DEBUG
-#include "jddebug.h"
-
-#include "ssl.h"
-
-using namespace JDLIB;
-
-
-#ifdef USE_GNUTLS
-
-// gnutls 使用
-
-void JDLIB::init_ssl()
-{
-#ifdef _DEBUG
-    std::cout << "init_ssl(gnutls)\n";
-#endif
-
-    gnutls_global_init();
-}
-
-void JDLIB::deinit_ssl()
-{
-#ifdef _DEBUG
-    std::cout << "deinit_ssl(gnutls)\n";
-#endif
-
-    gnutls_global_deinit();
-}
-
-
-JDSSL::JDSSL()
-    : m_session( 0 ),
-      m_cred( 0 )
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::JDSSL(gnutls)\n";
-#endif
-}
-
-
-JDSSL::~JDSSL()
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::~JDSSL(gnutls)\n";
-#endif
-
-    close();
-}
-
-
-const bool JDSSL::connect( const int soc )
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::connect(gnutls)\n";
-#endif
-
-    if( soc < 0 ) return false;
-    if( m_session ) return false;
-    if( m_cred ) return false;
-
-    int ret;
-
-    ret = gnutls_init( &m_session, GNUTLS_CLIENT );
-    if( ret != 0 ){
-        m_errmsg = "gnutls_init failed";
-        return false;
-    }
-
-#if GNUTLS_VERSION_NUMBER >= 0x020108
-    // gnutls >= 2.1.7 (unreleased)
-    gnutls_priority_set_direct( m_session, "NORMAL:%COMPAT", NULL );
-#else // GNUTLS_VERSION_NUMBER >= 0x020108
-    static const int priority_prot[] = { GNUTLS_SSL3, 0 };
-    // DEPRECATED (gnutls >= 2.1.4 gnutls =< 2.1.6)
-    // UNDEPRECATED (gnutls >= 2.1.7)
-    gnutls_set_default_priority( m_session );
-    // _GNUTLS_GCC_ATTR_DEPRECATE (gnutls >= 2.12.0)
-    gnutls_protocol_set_priority( m_session, priority_prot );
-#endif // GNUTLS_VERSION_NUMBER >= 0x020108
-
-    gnutls_transport_set_ptr( m_session, (gnutls_transport_ptr_t)(long) soc );
-    gnutls_certificate_allocate_credentials( &m_cred );
-    gnutls_credentials_set( m_session, GNUTLS_CRD_CERTIFICATE, m_cred );
-
-    while ( ( ret = gnutls_handshake( m_session ) ) != GNUTLS_E_SUCCESS )
-    {
-        if ( gnutls_error_is_fatal( ret ) != 0 )
-        {
-            m_errmsg = "gnutls_handshake failed";
-            return false;
-        }
-    }
-
-#ifdef _DEBUG
-    std::cout << "connect ok\n";
-#endif
-
-    return true;
-}
-
-
-const bool JDSSL::close()
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::close(gnutlsl)\n";
-#endif
-
-    if( m_session ){
-        gnutls_bye( m_session, GNUTLS_SHUT_RDWR );
-        gnutls_deinit( m_session );
-        m_session = 0;
-    }
-    if( m_cred ){
-        gnutls_certificate_free_credentials( m_cred );
-        m_cred = 0;
-    }
-
-    return true;
-}
-
-
-const int JDSSL::write( const char* buf, const size_t bufsize )
-{
-    int tmpsize = gnutls_record_send( m_session, buf, bufsize );
-    if( tmpsize < 0 ) m_errmsg = "gnutls_record_send failed";
-
-    return tmpsize;
-}
-
-
-const int JDSSL::read( char* buf, const size_t bufsize )
-{
-    int tmpsize = gnutls_record_recv( m_session, buf, bufsize );
-    if( tmpsize < 0 ) m_errmsg = "gnutls_record_recv failed";
-
-    return tmpsize;
-}
-
-#else ////////////////////////////////////////////////////////////////////
-
-// OpenSSL 使用
-
-void JDLIB::init_ssl()
-{
-#ifdef _DEBUG
-    std::cout << "init_ssl(openssl)\n";
-#endif
-}
-
-void JDLIB::deinit_ssl()
-{
-#ifdef _DEBUG
-    std::cout << "deinit_ssl(openssl)\n";
-#endif
-}
-
-
-JDSSL::JDSSL()
-    : m_ctx( NULL ),
-      m_ssl( NULL )
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::JDSSL(openssl)\n";
-#endif
-}
-
-
-JDSSL::~JDSSL()
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::~JDSSL(openssl)\n";
-#endif
-
-    close();
-}
-
-
-const bool JDSSL::connect( const int soc )
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::connect(openssl)\n";
-#endif
-
-    if( soc < 0 ) return false;
-    if( m_ctx ) return false;
-    if( m_ssl ) return false;
-
-    SSL_library_init();
-    m_ctx = SSL_CTX_new( SSLv23_client_method() );
-    if( ! m_ctx ){
-        m_errmsg = "SSL_CTX_new failed";
-        return false;
-    }
-
-    m_ssl = SSL_new( m_ctx );
-    if( ! m_ssl ){
-        m_errmsg = "SSL_new failed";
-        return false;
-    }
-
-    if( SSL_set_fd( m_ssl, soc ) == 0 ){
-        m_errmsg = "SSL_set_fd failed";
-        return false;
-    }
-
-    if( SSL_connect( m_ssl ) != 1 ){
-        m_errmsg = "SSL_connect failed";
-        return false;
-    }
-
-#ifdef _DEBUG
-    std::cout << "connect ok\n";
-#endif
-
-    return true;
-}
-
-
-const bool JDSSL::close()
-{
-#ifdef _DEBUG
-    std::cout << "JDSSL::close(openssl)\n";
-#endif
-
-    if( m_ssl ){
-        SSL_shutdown( m_ssl );
-        SSL_free( m_ssl );
-        m_ssl = NULL;
-    }
-    if( m_ctx ){
-        SSL_CTX_free( m_ctx );
-        m_ctx = NULL;
-    }
-
-    return true;
-}
-
-
-const int JDSSL::write( const char* buf, const size_t bufsize )
-{
-    int tmpsize = SSL_write( m_ssl, buf, bufsize );
-    if( tmpsize < 0 ) m_errmsg = "SSL_write failed";
-
-    return tmpsize;
-}
-
-
-const int JDSSL::read( char* buf, const size_t bufsize )
-{
-    int tmpsize = SSL_read( m_ssl, buf, bufsize );
-    if( tmpsize < 0 ) m_errmsg = "SSL_read failed";
-
-    return tmpsize;
-}
-
-#endif
diff -aurNZ jd-2.8.9-150226/src/jdlib/ssl.h jd-2.8.9-a171004/src/jdlib/ssl.h
--- jd-2.8.9-150226/src/jdlib/ssl.h	2012-08-04 00:17:43.000000000 +0900
+++ jd-2.8.9-a171004/src/jdlib/ssl.h	1970-01-01 09:00:00.000000000 +0900
@@ -1,62 +0,0 @@
-// ライセンス: GPL2
-
-//
-// SSL ローダ
-//
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef USE_GNUTLS
-#include <gnutls/gnutls.h>
-#else
-#include <openssl/ssl.h>
-
-// gdkmm/device.h で定義される set_key マクロと衝突する
-#ifdef set_key
-#undef set_key
-#endif
-
-#endif
-
-#include <string>
-
-namespace JDLIB
-{
-    class JDSSL
-    {
-        std::string m_errmsg;
-
-#ifdef USE_GNUTLS
-        gnutls_session_t m_session;
-
-#if GNUTLS_VERSION_NUMBER >= 0x020000
-        gnutls_certificate_credentials_t m_cred;
-#else // GNUTLS_VERSION_NUMBER >= 0x020000
-        // DEPRECATED (gnutls >= 2.x)
-        gnutls_certificate_credentials m_cred;
-#endif // GNUTLS_VERSION_NUMBER >= 0x020000
-
-#else // USE_GNUTLS
-        SSL_CTX *m_ctx;
-        SSL* m_ssl;
-#endif // USE_GNUTLS
-
-      public:
-
-        JDSSL();
-        virtual ~JDSSL();
-
-        const std::string& get_errmsg(){ return m_errmsg; }
-
-        const bool connect( const int soc );
-        const bool close();
-
-        const int write( const char* buf, const size_t bufsize );
-        const int read( char* buf, const size_t bufsize );
-    };
-
-    void init_ssl();
-    void deinit_ssl();
-}
diff -aurNZ jd-2.8.9-150226/src/jdversion.h jd-2.8.9-a171004/src/jdversion.h
--- jd-2.8.9-150226/src/jdversion.h	2015-02-26 23:01:05.000000000 +0900
+++ jd-2.8.9-a171004/src/jdversion.h	2017-10-04 17:50:08.674008707 +0900
@@ -16,8 +16,8 @@
 #define MAJORVERSION 2
 #define MINORVERSION 8
 #define MICROVERSION 9
-#define JDDATE    "150226"
-#define JDTAG     ""
+#define JDDATE    "171004"
+#define JDTAG     "a"
 
 //---------------------------------
 
@@ -26,7 +26,7 @@
 
 //---------------------------------
 
-#define JDCOMMENT "JD は gtkmm/GTK+2 を用いた2chブラウザです。"
+#define JDCOMMENT "JD は gtkmm/GTK+2 を用いた5chブラウザです。"
 #define JDCOPYRIGHT "(c) 2006-2015 JD project"
 #define JDBBS CONFIG::get_url_jdhp()+"cgi-bin/bbs/support/"
 #define JD2CHLOG CONFIG::get_url_jdhp()+"old2ch/"
diff -aurNZ jd-2.8.9-150226/src/linkfiltermanager.cpp jd-2.8.9-a171004/src/linkfiltermanager.cpp
--- jd-2.8.9-150226/src/linkfiltermanager.cpp	2012-02-14 00:24:32.000000000 +0900
+++ jd-2.8.9-a171004/src/linkfiltermanager.cpp	2016-03-25 20:04:33.501108000 +0900
@@ -144,8 +144,8 @@
     std::vector< LinkFilterItem >::iterator it = m_list_cmd.begin();
     for( ; it != m_list_cmd.end(); ++it ){
 
-        const std::string query = ( *it ).url;
-        const std::string cmd = ( *it ).cmd;
+        const std::string& query = ( *it ).url;
+        const std::string& cmd = ( *it ).cmd;
 
 #ifdef _DEBUG
         std::cout << "query = " << query << std::endl
@@ -154,15 +154,7 @@
         if( ! regex.exec( query, link, offset, icase, newline, usemigemo, wchar ) ) continue;
 
         // \0 ... \9 までのcmd文字列を置換
-        std::string cmd_out = cmd;
-        char rep_text[] = "\\0";
-        for( int i = 0; i < 9; i++ ){
-            if( regex.pos( i ) == -1 ){
-                continue;
-            }
-            rep_text[ 1 ] = '0' + i;
-            cmd_out = MISC::replace_str( cmd_out, rep_text, regex.str( i ) );
-        }
+        std::string cmd_out = regex.replace( cmd );
 
         // queryと一致したら実行
         CORE::get_usrcmd_manager()->exec( cmd_out, url, link, selection, 0 );
diff -aurNZ jd-2.8.9-150226/src/login2ch.cpp jd-2.8.9-a171004/src/login2ch.cpp
--- jd-2.8.9-150226/src/login2ch.cpp	2011-01-12 23:57:43.000000000 +0900
+++ jd-2.8.9-a171004/src/login2ch.cpp	2015-12-28 18:57:12.519376000 +0900
@@ -15,7 +15,10 @@
 
 #include "jdlib/loaderdata.h"
 #include "jdlib/miscmsg.h"
+#include "jdlib/misctrip.h"
+#include "jdlib/miscutil.h"
 
+#include <ctime>
 #include <cstring>
 
 enum
@@ -49,7 +52,7 @@
 
 Login2ch::Login2ch()
     : SKELETON::Login( URL_LOGIN2CH )
-    , m_rawdata( 0 ), m_lng_rawdata( 0 )
+    , m_rawdata( 0 ), m_lng_rawdata( 0 ), m_sid_expire( 0 )
 {
 #ifdef _DEBUG
     std::cout << "Login2ch::Login2ch\n";
@@ -81,6 +84,7 @@
     SKELETON::Login::set_login_now( false );
     SKELETON::Login::set_sessionid( std::string() );
     SESSION::set_login2ch( false );
+    m_sid_expire = 0;
 }
 
 
@@ -92,7 +96,7 @@
     if( is_loading() ) return;
 
 #ifdef _DEBUG
-    std::cout << "Login2ch::start_login url = " << CONFIG::get_url_login2ch() << std::endl;
+    std::cout << "Login2ch::start_login url = " << CONFIG::get_url_api2ch() << "/v1/auth" << std::endl;
 #endif 
 
     set_str_code( "" );
@@ -103,24 +107,60 @@
         finish();
         return;
     }
-    if( CONFIG::get_url_login2ch().empty() || get_username().empty() || get_passwd().empty() ){
+    if( CONFIG::get_url_api2ch().empty()
+            || CONFIG::get_appkey().empty() || CONFIG::get_hmkey().empty()
+            /*|| get_username().empty() || get_passwd().empty()*/ ){
 
         finish();
         return;
     }
 
+    update_sid( true );
+}
+
+//
+// Session-ID更新
+//
+void Login2ch::update_sid( const bool force )
+{
+
+    if( ( ! force && ! login_now() ) || ! SESSION::is_online() ){
+        // ログイン中でないかオフラインなら何もしない
+        return;
+    }
+
+    time_t ct = time(0);
+    if( login_now() && m_sid_expire > ct ){
+
+        // ログイン中は更新期限前なら抜ける
+        return;
+    }
+
     JDLIB::LOADERDATA data;
     data.init_for_data();
-    data.url = CONFIG::get_url_login2ch();
-    data.agent = "DOLIB/1.00";
-    data.ex_field = "X-2ch-UA: " + CONFIG::get_x_2ch_ua() + "\r\n";
-
-    data.str_post = "ID=";
-    data.str_post += get_username();
-    data.str_post += "&PW=";
-    data.str_post += get_passwd();
+    data.url = CONFIG::get_url_api2ch() + "/v1/auth/";
+    data.agent = CONFIG::get_agent_for2ch();
+    data.contenttype = "application/x-www-form-urlencoded";
+    if( ! CONFIG::get_x_2ch_ua().empty() )
+        data.ex_field = "X-2ch-UA: " + CONFIG::get_x_2ch_ua() + "\r\n";
+
+    std::string msg = CONFIG::get_appkey() + MISC::itostr( ct );
+
+    data.str_post = "ID=" + MISC::url_encode( get_username() )
+        + "&PW=" + MISC::url_encode( get_passwd() )
+        + "&KY=" + CONFIG::get_appkey()
+        + "&CT=" + MISC::itostr( ct )
+        + "&HB=" + MISC::hmac_sha256( CONFIG::get_hmkey(), msg );
+#ifdef _DEBUG
+//    std::cout << "post data = " << data.str_post << std::endl;
+#endif
+
+    if( CONFIG::get_use_proxy_for2ch() ){
+        data.host_proxy = CONFIG::get_proxy_for2ch();
+        data.port_proxy = CONFIG::get_proxy_port_for2ch();
+        data.basicauth_proxy = CONFIG::get_proxy_basicauth_for2ch();
+    }
 
-    logout();
     if( ! m_rawdata ) m_rawdata = ( char* )malloc( SIZE_OF_RAWDATA );
     memset( m_rawdata, 0, SIZE_OF_RAWDATA );
     m_lng_rawdata = 0;
@@ -180,18 +220,18 @@
 #ifdef _DEBUG
 //            std::cout << "sid = " << sid << std::endl;
 #endif
-            if( sid.find( "ERROR" ) != 0 ){
-                SKELETON::Login::set_login_now( true );
-                SKELETON::Login::set_sessionid( sid );
-                show_err = false;
-                SESSION::set_login2ch( true );
-            }
-            else{
-                MISC::ERRMSG( "2chログイン失敗 : sid = " + sid );
-                set_str_code( get_str_code() + "\nIDとパスワードを確認して下さい" );
-            }
+            SKELETON::Login::set_login_now( true );
+            SKELETON::Login::set_sessionid( sid );
+            show_err = false;
+            SESSION::set_login2ch( true );
+
+            m_sid_expire = time(0) + 6*60*60; // とりあえず 6 hour
+        }
+        else if( sid.find( "ng (" ) == 0 ){
+            MISC::ERRMSG( "2ch認証失敗 : " + sid );
+            set_str_code( get_str_code() + "\nIDとパスワードまたはAppKeyとHMKeyを確認して下さい" );
         }
-        else set_str_code( get_str_code() + "\n認証サーバーのURLを確認して下さい" );
+        else set_str_code( get_str_code() + "\n2ch APIサーバのURLを確認して下さい" );
     }
 
     // エラー表示
@@ -199,15 +239,19 @@
         SKELETON::MsgDiag mdiag( NULL, "オフラインです" );
         mdiag.run();
     }
-    else if( get_username().empty() || get_passwd().empty() ){
-        SKELETON::MsgDiag mdiag( NULL, "IDまたはパスワードが設定されていません\n\n設定→ネットワーク→パスワードで設定してください" );
+    else if( CONFIG::get_url_api2ch().empty() ){
+        SKELETON::MsgDiag mdiag( NULL, "2ch APIのアドレスが設定されていません\n\n設定→about:config 高度な設定で設定してください" );
         mdiag.run();
     }
-    else if( CONFIG::get_url_login2ch().empty() ){
-        SKELETON::MsgDiag mdiag( NULL, "2chの認証サーバのURLが指定されていません。" );
+    else if( CONFIG::get_appkey().empty() || CONFIG::get_hmkey().empty() ){
+        SKELETON::MsgDiag mdiag( NULL, "AppKeyまたはHMKeyが設定されていません\n\n設定→about:config 高度な設定で設定してください" );
         mdiag.run();
     }
-    else if( show_err ){
+//    else if( get_username().empty() || get_passwd().empty() ){
+//        SKELETON::MsgDiag mdiag( NULL, "IDまたはパスワードが設定されていません\n\n設定→ネットワーク→パスワードで設定してください" );
+//        mdiag.run();
+//    }
+    else if( show_err && ! login_now() ){
         std::string str_err = "ログインに失敗しました。\n";
         str_err += get_str_code();
         SKELETON::MsgDiag mdiag( NULL, str_err );
diff -aurNZ jd-2.8.9-150226/src/login2ch.h jd-2.8.9-a171004/src/login2ch.h
--- jd-2.8.9-150226/src/login2ch.h	2008-06-01 02:10:46.000000000 +0900
+++ jd-2.8.9-a171004/src/login2ch.h	2015-04-13 12:36:38.082321000 +0900
@@ -11,12 +11,15 @@
 
 #include "skeleton/login.h"
 
+#include <ctime>
+
 namespace CORE
 {
     class Login2ch : public SKELETON::Login
     {
         char* m_rawdata;
         int m_lng_rawdata;
+        time_t m_sid_expire;
 
       public:
 
@@ -25,6 +28,7 @@
 
         virtual void start_login();
         virtual void logout();
+        void update_sid( const bool force = false );
 
       private:
 
diff -aurNZ jd-2.8.9-150226/src/loginbe.cpp jd-2.8.9-a171004/src/loginbe.cpp
--- jd-2.8.9-150226/src/loginbe.cpp	2011-01-12 23:57:43.000000000 +0900
+++ jd-2.8.9-a171004/src/loginbe.cpp	2015-12-30 00:33:24.389742000 +0900
@@ -121,7 +121,7 @@
     data.contenttype = "application/x-www-form-urlencoded";
     data.str_post = "m=" + MISC::url_encode( get_username() );
     data.str_post += "&p=" + MISC::url_encode( get_passwd() );
-    data.str_post += "&submit=" + MISC::charset_url_encode( "登録", "EUC-JP" );
+    data.str_post += "&submit=" + MISC::url_encode( std::string( "登録" ), CHARCODE_EUCJP );
 
     logout();
     if( ! m_rawdata ) m_rawdata = ( char* )malloc( SIZE_OF_RAWDATA );
diff -aurNZ jd-2.8.9-150226/src/loginp2.cpp jd-2.8.9-a171004/src/loginp2.cpp
--- jd-2.8.9-150226/src/loginp2.cpp	2012-01-29 13:18:32.000000000 +0900
+++ jd-2.8.9-a171004/src/loginp2.cpp	2015-12-30 00:45:59.069731000 +0900
@@ -133,8 +133,7 @@
     data.str_post += "&regist_cookie=1"; // p2(rep2)
     data.str_post += "&ctl_register_cookie=1"; // p2.2ch.net(2012/01/24修正)
     data.str_post += "&register_cookie=1"; // p2.2ch.net(2012/01/24修正)
-//    data.str_post += "&submit_member=" + MISC::charset_url_encode( "ユーザログイン", "MS932" );  // 2009/12/20 仕様変更
-    data.str_post += "&submit_userlogin=" + MISC::charset_url_encode( "ユーザログイン", "MS932" );
+    data.str_post += "&submit_userlogin=" + MISC::url_encode( std::string( "ユーザログイン" ), CHARCODE_SJIS );
 
     logout();
     if( ! m_rawdata ) m_rawdata = ( char* )malloc( SIZE_OF_RAWDATA );
diff -aurNZ jd-2.8.9-150226/src/main.cpp jd-2.8.9-a171004/src/main.cpp
--- jd-2.8.9-150226/src/main.cpp	2015-01-31 13:05:00.000000000 +0900
+++ jd-2.8.9-a171004/src/main.cpp	2016-04-12 01:52:15.023407000 +0900
@@ -4,6 +4,10 @@
 //#define _DEBUG_MEM_PROFILE
 #include "jddebug.h"
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include "config/globalconf.h"
 
 #include "winmain.h"
@@ -13,7 +17,7 @@
 
 #include "jdlib/miscmsg.h"
 #include "jdlib/miscutil.h"
-#include "jdlib/ssl.h"
+#include "jdlib/jdsocket.h"
 #include "jdlib/jdregex.h"
 
 #include <signal.h>
@@ -481,6 +485,10 @@
     }
 #endif
 
+    // デフォルトのbackend指定
+    Glib::setenv( "PANGOCAIRO_BACKEND", "fc", 0 );
+
+#if !GLIB_CHECK_VERSION(2,45,5)
 #ifdef _DEBUG_MEM_PROFILE
     g_mem_set_vtable( glib_mem_profiler_table );
     atexit( g_mem_profile );
@@ -495,6 +503,7 @@
     vtable.try_realloc = realloc;
     g_mem_set_vtable( &vtable );
 #endif
+#endif
 
     Gtk::Main m( &argc, &argv );
 
@@ -524,7 +533,7 @@
     else MISC::ERRMSG( "failed to connect to gnome session manager" );
 #endif
 
-    JDLIB::init_ssl();
+    JDLIB::tlslib_init();
 
     // 全体設定ロード
     bool init = !( CONFIG::load_conf() );
@@ -629,7 +638,7 @@
     xsmp_session_end( &xsmpdata );
 #endif
 
-    JDLIB::deinit_ssl();
+    JDLIB::tlslib_deinit();
 
     return 0;
 }
diff -aurNZ jd-2.8.9-150226/src/maintoolbar.cpp jd-2.8.9-a171004/src/maintoolbar.cpp
--- jd-2.8.9-150226/src/maintoolbar.cpp	2011-06-21 00:21:47.000000000 +0900
+++ jd-2.8.9-a171004/src/maintoolbar.cpp	2017-04-05 21:30:44.244434000 +0900
@@ -65,6 +65,11 @@
     pack_buttons();
 }
 
+
+MainToolBar::~MainToolBar()
+{}
+
+
 // ボタンのパッキング
 // virtual
 void MainToolBar::pack_buttons()
diff -aurNZ jd-2.8.9-150226/src/maintoolbar.h jd-2.8.9-a171004/src/maintoolbar.h
--- jd-2.8.9-150226/src/maintoolbar.h	2011-06-21 00:21:47.000000000 +0900
+++ jd-2.8.9-a171004/src/maintoolbar.h	2017-04-05 21:30:08.380434000 +0900
@@ -41,7 +41,7 @@
       public:
 
         MainToolBar(); 
-        virtual ~MainToolBar(){}
+        virtual ~MainToolBar();
 
       protected:
 
diff -aurNZ jd-2.8.9-150226/src/menuslots.cpp jd-2.8.9-a171004/src/menuslots.cpp
--- jd-2.8.9-150226/src/menuslots.cpp	2014-03-29 15:36:22.000000000 +0900
+++ jd-2.8.9-a171004/src/menuslots.cpp	2016-02-01 00:21:24.798988000 +0900
@@ -686,6 +686,21 @@
 
 
 //
+// 2chスレ取得のread.cgiモードの切り替え
+//
+void Core::slot_toggle_use_2ch_readcgi()
+{
+    CONFIG::set_use_2ch_readcgi( ! CONFIG::get_use_2ch_readcgi() );
+
+    if( CONFIG::get_use_2ch_readcgi() ){
+
+        SKELETON::MsgDiag mdiag( NULL, "read.cgiを使用すると以下の問題が生じるので注意して下さい。\n\n(1) 空白文字が欠落することがあるのでAAが崩れます\n(2) あぼ〜ん発生でログ破損がない代わりに検出が出来なくなります\n(3) 文字化けが発生したりHTMLタグが欠落する場合が有ります\n(4) 通常モードに戻してスレを更新するとログが破損します" );
+        mdiag.run();
+    }
+}
+
+
+//
 // まちBBSのofflawモードの切り替え
 //
 void Core::slot_toggle_use_machi_offlaw()
@@ -1041,6 +1056,17 @@
 
 
 //
+// 置換文字列の編集
+//
+void Core::slot_replace_pref()
+{
+    SKELETON::PrefDiag* pref= CORE::PrefDiagFactory( NULL, CORE::PREFDIAG_REPLACESTR, URL_REPLACESTR );
+    pref->run();
+    delete pref;
+}
+
+
+//
 // about:config
 //
 void Core::slot_aboutconfig()
@@ -1141,7 +1167,7 @@
 //
 void Core::slot_show_cache_board()
 {
-    const std::string url = DBTREE::url_subject( BOARD::get_admin()->get_current_url() );
+    const std::string url = DBTREE::url_boardbase( BOARD::get_admin()->get_current_url() );
     if( ! url.empty() ) CORE::core_set_command( "open_board_showlog", url );
 }
 
@@ -1269,7 +1295,7 @@
 //
 void Core::slot_show_bbs()
 {
-    CORE::core_set_command( "open_board" , DBTREE::url_subject( ENVIRONMENT::get_jdbbs() ), "newtab" );
+    CORE::core_set_command( "open_board" , DBTREE::url_boardbase( ENVIRONMENT::get_jdbbs() ), "newtab" );
 }
 
 
@@ -1278,7 +1304,7 @@
 //
 void Core::slot_show_old2ch()
 {
-    CORE::core_set_command( "open_board" , DBTREE::url_subject( ENVIRONMENT::get_jd2chlog() ), "newtab" );
+    CORE::core_set_command( "open_board" , DBTREE::url_boardbase( ENVIRONMENT::get_jd2chlog() ), "newtab" );
 }
 
 
diff -aurNZ jd-2.8.9-150226/src/message/Makefile.am jd-2.8.9-a171004/src/message/Makefile.am
--- jd-2.8.9-150226/src/message/Makefile.am	2013-01-06 00:44:26.000000000 +0900
+++ jd-2.8.9-a171004/src/message/Makefile.am	2017-04-11 20:45:04.061697000 +0900
@@ -17,6 +17,7 @@
 	messageview.h \
 	post.h \
 	toolbar.h \
+	logitem.h \
 	logmanager.h \
 	confirmdiag.h
 
diff -aurNZ jd-2.8.9-150226/src/message/confirmdiag.cpp jd-2.8.9-a171004/src/message/confirmdiag.cpp
--- jd-2.8.9-150226/src/message/confirmdiag.cpp	2011-02-24 22:50:01.000000000 +0900
+++ jd-2.8.9-a171004/src/message/confirmdiag.cpp	2017-04-05 22:37:13.412372000 +0900
@@ -30,6 +30,10 @@
     grab_ok();
 }
 
+ConfirmDiag::~ConfirmDiag()
+{}
+
+
 void ConfirmDiag::slot_switch_page( GtkNotebookPage*, guint page )
 {
     if( get_notebook().get_nth_page( page ) == get_detail() ){
diff -aurNZ jd-2.8.9-150226/src/message/confirmdiag.h jd-2.8.9-a171004/src/message/confirmdiag.h
--- jd-2.8.9-150226/src/message/confirmdiag.h	2011-02-24 22:50:01.000000000 +0900
+++ jd-2.8.9-a171004/src/message/confirmdiag.h	2017-04-05 21:28:01.580436000 +0900
@@ -18,6 +18,7 @@
       public:
 
         ConfirmDiag( const std::string& url, const std::string& message );
+        virtual ~ConfirmDiag();
 
         Gtk::CheckButton& get_chkbutton(){ return m_chkbutton; }
 
diff -aurNZ jd-2.8.9-150226/src/message/logitem.h jd-2.8.9-a171004/src/message/logitem.h
--- jd-2.8.9-150226/src/message/logitem.h	2010-10-24 01:04:24.000000000 +0900
+++ jd-2.8.9-a171004/src/message/logitem.h	2015-12-30 17:47:15.352792000 +0900
@@ -9,6 +9,7 @@
 
 #include "messageadmin.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 
 #include <list>
diff -aurNZ jd-2.8.9-150226/src/message/messageview.cpp jd-2.8.9-a171004/src/message/messageview.cpp
--- jd-2.8.9-150226/src/message/messageview.cpp	2010-10-15 00:45:52.000000000 +0900
+++ jd-2.8.9-a171004/src/message/messageview.cpp	2016-02-10 16:00:04.454205000 +0900
@@ -31,11 +31,13 @@
     // ツールバーのスレタイトルを編集不可にする
     MESSAGE::get_admin()->show_entry_new_subject( false );
 
+    const std::string& subject = DBTREE::article_modified_subject( get_url() );
+
     // メインウィンドウのタイトルに表示する文字
-    set_title( "[ 書き込み ] " + DBTREE::article_subject( get_url() ) );
+    set_title( "[ 書き込み ] " + MISC::to_plain( subject ) );
 
     // ツールバーにスレ名を表示
-    set_label( DBTREE::article_subject( get_url() ) );
+    set_label( MISC::to_markup( subject ), true );
 }
 
 
@@ -152,7 +154,7 @@
     MESSAGE::get_admin()->show_entry_new_subject( true );
 
     // メインウィンドウのタイトルに表示する文字
-    set_title( "[ 新スレ作成 ] " + DBTREE::article_subject( get_url() ) );
+    set_title( "[ 新スレ作成 ] " + MISC::to_plain( DBTREE::article_modified_subject( get_url() ) ) );
 }
 
 
@@ -202,5 +204,5 @@
 
 void MessageViewNew::reload()
 {
-    CORE::core_set_command( "open_board", DBTREE::url_subject( get_url() ), "true" );
+    CORE::core_set_command( "open_board", DBTREE::url_boardbase( get_url() ), "true" );
 }
diff -aurNZ jd-2.8.9-150226/src/message/messageviewbase.cpp jd-2.8.9-a171004/src/message/messageviewbase.cpp
--- jd-2.8.9-150226/src/message/messageviewbase.cpp	2012-08-03 23:40:01.000000000 +0900
+++ jd-2.8.9-a171004/src/message/messageviewbase.cpp	2016-03-16 01:03:47.098528000 +0900
@@ -15,6 +15,7 @@
 #include "skeleton/editview.h"
 #include "skeleton/detaildiag.h"
 
+#include "jdlib/misccharcode.h"
 #include "jdlib/miscutil.h"
 #include "jdlib/misctime.h"
 #include "jdlib/misctrip.h"
@@ -85,7 +86,7 @@
     m_max_line = DBTREE::line_number( get_url() ) * 2;
     m_max_str = DBTREE::message_count( get_url() );
 
-    m_iconv = new JDLIB::Iconv( "UTF-8", DBTREE::board_charset( get_url() ) );;
+    m_iconv = new JDLIB::Iconv( CHARCODE_UTF8, DBTREE::board_charcode( get_url() ) );;
 
     m_lng_iconv = m_max_str * 3;
     if( ! m_lng_iconv ) m_lng_iconv = MAX_STR_ICONV;
@@ -145,7 +146,7 @@
 //
 // メインウィンドウのURLバーなどに表示する)
 //
-const std::string MessageViewBase::url_for_copy()
+const std::string MessageViewBase::url_for_copy() const
 {
     return DBTREE::url_readcgi( get_url(), 0, 0 );
 }
@@ -255,7 +256,7 @@
 // ロード中
 //
 // virtual
-const bool MessageViewBase::is_loading()
+const bool MessageViewBase::is_loading() const
 {
     if( ! m_post ) return false;
 
@@ -640,7 +641,7 @@
         const bool newline = true;
         const bool usemigemo = false;
         const bool wchar = false;
-        if( regex.exec( "%26%23[0-9]*%3b", msg, offset, icase, newline, usemigemo, wchar ) ){
+        if( regex.exec( "%26%23[0-9]+%3b", msg, offset, icase, newline, usemigemo, wchar ) ){
 
             SKELETON::MsgDiag mdiag( get_parent_win(),
                                      "ユニコード文字が含まれていますが、この板ではユニコード文字は文字化けします(BBS_UNICODE=change)。\n\n書き込みますか?",
@@ -728,7 +729,7 @@
 //
 // フォントの更新
 //
-void MessageViewBase::relayout()
+void MessageViewBase::relayout( const bool completely )
 {
     init_font( CONFIG::get_fontname( FONT_MESSAGE ) );
     init_color();
@@ -876,12 +877,6 @@
         std::string new_subject = MESSAGE::get_admin()->get_new_subject();
         if( ! new_subject.empty() ) set_label( new_subject );
 
-        // URLを除外してエスケープ
-        const bool include_url = false;
-        std::string msg;
-        if( m_text_message ) msg = MISC::html_escape( m_text_message->get_text(), include_url );
-        msg = MISC::replace_str( msg, "\n", " <br> " );
-
         std::stringstream ss;
 
         // 名前 + トリップ
@@ -896,7 +891,7 @@
             std::string trip;
             if( trip_pos != std::string::npos )
             {
-                trip = MISC::get_trip( name_field.substr( trip_pos + 1 ), DBTREE::board_charset( get_url() ) );
+                trip = MISC::get_trip( name_field.substr( trip_pos + 1 ), DBTREE::board_charcode( get_url() ) );
             }
 
             ss << name;
@@ -911,9 +906,29 @@
         struct timeval tv;
         struct timezone tz;
         gettimeofday( &tv, &tz );
-        ss << MISC::timettostr( tv.tv_sec, MISC::TIME_WEEK );
+        ss << MISC::timettostr( tv.tv_sec, MISC::TIME_WEEK ) << " ID:\?\?\?<>";
+
+        if( m_text_message ){
+            std::string msg = m_text_message->get_text();
+            if( DBTREE::get_unicode( get_url() ) == "change" ){
+                // MS932等に無い文字を数値参照にするために文字コードを変換する
+                int byte_out;
+                std::string str_enc = m_iconv->convert( msg.c_str(), msg.length(), byte_out );
+                msg = MISC::Iconv( str_enc, DBTREE::board_charcode( get_url() ), CHARCODE_UTF8 );
+            }
+            else{
+                // XXX 2chでは文字実態参照はデコードされない
+                msg = MISC::chref_decode( msg );
+            }
+
+            // URLを除外してエスケープ
+            const bool include_url = false;
+            msg = MISC::html_escape( msg, include_url );
+
+            ss << MISC::replace_str( msg, "\n", " <br> " );
+        }
 
-        ss << " ID:???" << "<>" << msg << "<>\n";
+        ss << "<>\n";
 
 #ifdef _DEBUG
         std::cout << ss.str() << std::endl;
@@ -982,7 +997,7 @@
     {
         int byte_out;
         const char* msgc = message.c_str();
-        std::string str_enc = m_iconv->convert( (char*)msgc, strlen( msgc ), byte_out );
+        std::string str_enc = m_iconv->convert( (char*)msgc, message.length(), byte_out );
         m_lng_str_enc = str_enc.length();
 
         // 特殊文字の文字数を計算
diff -aurNZ jd-2.8.9-150226/src/message/messageviewbase.h jd-2.8.9-a171004/src/message/messageviewbase.h
--- jd-2.8.9-150226/src/message/messageviewbase.h	2011-02-20 00:05:44.000000000 +0900
+++ jd-2.8.9-a171004/src/message/messageviewbase.h	2016-03-16 01:03:05.818528000 +0900
@@ -87,7 +87,7 @@
         virtual Gtk::Window* get_parent_win();
 
         // コピー用のURL
-        virtual const std::string url_for_copy();
+        virtual const std::string url_for_copy() const;
 
         // コマンド
         virtual const bool set_command( const std::string& command,
@@ -96,10 +96,10 @@
             );
 
         // ロード中
-        virtual const bool is_loading();
+        virtual const bool is_loading() const;
 
         // 規制中や行数や文字列がオーバーして書き込めない
-        virtual const bool is_broken(){ return ( ! m_str_pass.empty() || m_over_lines || m_over_lng ); }
+        virtual const bool is_broken() const { return ( ! m_str_pass.empty() || m_over_lines || m_over_lng ); }
 
         // キーを押した        
         virtual const bool slot_key_press( GdkEventKey* event );
@@ -107,7 +107,7 @@
         virtual void clock_in();
         virtual void write();
         virtual void reload(){}
-        virtual void relayout();
+        virtual void relayout( const bool completely = false );
         virtual void close_view();
         virtual void redraw_view();
         virtual void focus_view();
diff -aurNZ jd-2.8.9-150226/src/message/post.cpp jd-2.8.9-a171004/src/message/post.cpp
--- jd-2.8.9-150226/src/message/post.cpp	2014-03-23 02:42:53.000000000 +0900
+++ jd-2.8.9-a171004/src/message/post.cpp	2016-05-20 17:12:54.426605016 +0900
@@ -215,8 +215,7 @@
     std::cout << "Post::receive_finish\n";
 #endif
 
-    std::string charset = DBTREE::board_charset( m_url );
-    JDLIB::Iconv* libiconv = new JDLIB::Iconv( charset, "UTF-8" );
+    JDLIB::Iconv* libiconv = new JDLIB::Iconv( DBTREE::board_charcode( m_url ), CHARCODE_UTF8 );
     int byte_out;
     m_return_html = libiconv->convert( m_rawdata, m_lng_rawdata, byte_out );
     delete libiconv;
@@ -315,10 +314,8 @@
         newline = false; // . に改行をマッチさせる
         // Smaba24規制の場合
         //   ERROR - 593 60 sec たたないと書けません。(1回目、8 sec しかたってない)
-        // 忍法帖規制の場合 ( samba秒だけ取得する。 )
-        //   ERROR:修行が足りません(Lv=2)。しばらくたってから投稿してください。(48 sec)
-        //   この板のsambaは samba=30 sec
-        if( regex.exec( "ERROR( +- +593 +|:.+samba=)([0-9]+) +sec", m_errmsg, offset, icase, newline, usemigemo, wchar ) ){
+        //   ERROR: Samba24:Caution 25 秒たたないと書けません。(1 回目、24 秒しかたってない)
+        if( regex.exec( "(ERROR +- +593|ERROR: +Samba24:Caution|) +([0-9]+) +", m_errmsg, offset, icase, newline, usemigemo, wchar ) ){
             time_t sec = atoi( regex.str( 2 ).c_str() );
 #ifdef _DEBUG
             std::cout << "samba = " << sec << std::endl;
@@ -362,6 +359,10 @@
     std::cout << "location: [" << location() << "]\n";
 #endif
 
+    // クッキーのセット
+    const bool empty_cookies = DBTREE::board_list_cookies_for_write( m_url ).empty();
+    if( list_cookies.size() ) DBTREE::board_set_list_cookies_for_write( m_url, list_cookies );
+
     // 成功
     if( title.find( "書きこみました" ) != std::string::npos
         || tag_2ch.find( "true" ) != std::string::npos
@@ -372,9 +373,6 @@
         std::cout << "write ok" << std::endl;
 #endif        
 
-        // クッキーのセット
-        DBTREE::board_set_list_cookies_for_write( m_url, list_cookies );
-
         DBTREE::article_update_writetime( m_url );
         emit_sigfin();
         return;
@@ -384,7 +382,7 @@
     else if( m_count < 1 && // 永久ループ防止
         ( title.find( "書き込み確認" ) != std::string::npos
           || tag_2ch.find( "cookie" ) != std::string::npos
-          || ( ! DBTREE::board_list_cookies_for_write( m_url ).size() && list_cookies.size() )
+          || ( empty_cookies && list_cookies.size() )
             ) ){
 
         clear();
@@ -414,12 +412,9 @@
         DBTREE::board_analyze_keyword_for_write( m_url, m_return_html );
 
         // 現在のメッセージにキーワードが付加されていない時は付け加える
-        const std::string keyword = DBTREE::board_keyword_for_write( m_url );
+        const std::string& keyword = DBTREE::board_keyword_for_write( m_url );
         if( ! keyword.empty() && m_msg.find( keyword ) == std::string::npos ) m_msg += "&" + keyword;
 
-        // クッキーのセット
-        DBTREE::board_set_list_cookies_for_write( m_url, list_cookies );
-
         ++m_count; // 永久ループ防止
         post_msg();
 
@@ -434,12 +429,9 @@
         DBTREE::board_analyze_keyword_for_write( m_url, m_return_html );
 
         // 現在のメッセージにキーワードが付加されていない時は付け加える
-        const std::string keyword = DBTREE::board_keyword_for_write( m_url );
+        const std::string& keyword = DBTREE::board_keyword_for_write( m_url );
         if( ! keyword.empty() && m_msg.find( keyword ) == std::string::npos ) m_msg += "&" + keyword;
 
-        // クッキーのセット
-        DBTREE::board_set_list_cookies_for_write( m_url, list_cookies );
-
         // subbbs.cgi にポスト先を変更してもう一回ポスト
         m_subbbs = true;
         ++m_count; // 永久ループ防止
@@ -451,13 +443,10 @@
 
 #ifdef _DEBUG
     std::cout << "Error" << std::endl;
-    std::cout << m_errmsg << std::endl;
+    std::cout << m_return_html << std::endl;
 #endif        
 
-    // クッキー関係のエラーの時はクッキーをセット
-    if( tag_2ch.find( "cookie" ) != std::string::npos ) DBTREE::board_set_list_cookies_for_write( m_url, list_cookies );
-
-    MISC::ERRMSG( m_return_html );
+    MISC::ERRMSG( m_errmsg );
 
     set_code( HTTP_ERR );
     emit_sigfin();
diff -aurNZ jd-2.8.9-150226/src/message/toolbar.cpp jd-2.8.9-a171004/src/message/toolbar.cpp
--- jd-2.8.9-150226/src/message/toolbar.cpp	2011-09-23 18:54:03.000000000 +0900
+++ jd-2.8.9-a171004/src/message/toolbar.cpp	2017-04-05 21:27:38.684436000 +0900
@@ -84,6 +84,10 @@
 }
 
 
+MessageToolBar::~MessageToolBar()
+{}
+
+
 // 新規スレ名entry表示切り替え
 void MessageToolBar::show_entry_new_subject( bool show )
 {
@@ -236,6 +240,9 @@
 }
 
 
+MessageToolBarPreview::~MessageToolBarPreview()
+{}
+
 
 // ボタンのパッキング
 // virtual
diff -aurNZ jd-2.8.9-150226/src/message/toolbar.h jd-2.8.9-a171004/src/message/toolbar.h
--- jd-2.8.9-150226/src/message/toolbar.h	2008-09-21 20:57:21.000000000 +0900
+++ jd-2.8.9-a171004/src/message/toolbar.h	2017-04-05 21:26:25.680438000 +0900
@@ -59,7 +59,7 @@
       public:
 
         MessageToolBar();
-        virtual ~MessageToolBar(){}
+        virtual ~MessageToolBar();
 
         void show_entry_new_subject( bool show );
         std::string get_new_subject();
@@ -83,7 +83,7 @@
       public:
 
         MessageToolBarPreview();
-        virtual ~MessageToolBarPreview(){}
+        virtual ~MessageToolBarPreview();
 
       protected:
 
diff -aurNZ jd-2.8.9-150226/src/passwdpref.h jd-2.8.9-a171004/src/passwdpref.h
--- jd-2.8.9-150226/src/passwdpref.h	2014-03-23 01:51:58.000000000 +0900
+++ jd-2.8.9-a171004/src/passwdpref.h	2015-05-20 21:03:42.635093000 +0900
@@ -33,7 +33,7 @@
 
       PasswdFrame2ch()
       : m_label_sid_2ch( false, "SID: ", CORE::get_login2ch()->get_sessionid() ),
-        entry_id( true, "ユーザID(_I): " ), entry_passwd( true, "秘密鍵(_P): " )
+        entry_id( true, "ユーザID(_I): " ), entry_passwd( true, "パスワード(_P): " )
         {
             const int mrg = 8;
 
diff -aurNZ jd-2.8.9-150226/src/prefdiagfactory.cpp jd-2.8.9-a171004/src/prefdiagfactory.cpp
--- jd-2.8.9-150226/src/prefdiagfactory.cpp	2011-06-03 00:05:53.000000000 +0900
+++ jd-2.8.9-a171004/src/prefdiagfactory.cpp	2016-02-01 02:20:19.526879000 +0900
@@ -6,6 +6,7 @@
 #include "privacypref.h"
 #include "browserpref.h"
 #include "linkfilterpref.h"
+#include "replacestrpref.h"
 #include "usrcmdpref.h"
 #include "proxypref.h"
 #include "globalabonepref.h"
@@ -51,6 +52,9 @@
         case PREFDIAG_LINKFILTER:
             return new CORE::LinkFilterPref( parent, url );
 
+        case PREFDIAG_REPLACESTR:
+            return new CORE::ReplaceStrPref( parent, url );
+
         case PREFDIAG_USRCMD:
             return new CORE::UsrCmdPref( parent, url );
 
diff -aurNZ jd-2.8.9-150226/src/prefdiagfactory.h jd-2.8.9-a171004/src/prefdiagfactory.h
--- jd-2.8.9-150226/src/prefdiagfactory.h	2011-06-03 00:05:53.000000000 +0900
+++ jd-2.8.9-a171004/src/prefdiagfactory.h	2016-02-01 00:19:32.210990000 +0900
@@ -20,6 +20,7 @@
         PREFDIAG_PRIVACY,
         PREFDIAG_BROWSER,
         PREFDIAG_LINKFILTER,
+        PREFDIAG_REPLACESTR,
         PREFDIAG_USRCMD,
         PREFDIAG_PROXY,
         PREFDIAG_GLOBALABONETHREAD,
diff -aurNZ jd-2.8.9-150226/src/replacestrmanager.cpp jd-2.8.9-a171004/src/replacestrmanager.cpp
--- jd-2.8.9-150226/src/replacestrmanager.cpp	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/replacestrmanager.cpp	2017-10-04 17:34:41.938022914 +0900
@@ -0,0 +1,330 @@
+// ライセンス: GPL2
+
+//#define _DEBUG
+#include "jddebug.h"
+
+#include "replacestrmanager.h"
+#include "cache.h"
+#include "type.h"
+#include "command.h"
+
+#include "jdlib/miscmsg.h"
+#include "jdlib/miscutil.h"
+
+#include "xml/document.h"
+#include "xml/tools.h"
+
+#define ROOT_NODE_NAME_REPLACESTR "replacestrlist"
+
+static const char* const target_str[] = { "subject", "name", "mail", "date", "msg" };
+
+
+static CORE::ReplaceStr_Manager* instance_replacestr_manager = NULL;
+
+CORE::ReplaceStr_Manager* CORE::get_replacestr_manager()
+{
+    if( ! instance_replacestr_manager ) instance_replacestr_manager = new ReplaceStr_Manager();
+    assert( instance_replacestr_manager );
+
+    return instance_replacestr_manager;
+}
+
+
+void CORE::delete_replacestr_manager()
+{
+    if( instance_replacestr_manager ) delete instance_replacestr_manager;
+    instance_replacestr_manager = NULL;
+}
+
+///////////////////////////////////////////////
+
+using namespace CORE;
+
+ReplaceStr_Manager::ReplaceStr_Manager()
+{
+    for(int i = 0; i < REPLACETARGET_MAX; ++i ) m_chref[ i ] = false;
+
+    std::string xml;
+    if( CACHE::load_rawdata( CACHE::path_replacestr(), xml ) ) xml2list( xml );
+    else{
+        // デフォルトの設定を行う
+        m_chref[ 0 ] = true;
+        ReplaceStrCondition condition = { { false, false, false, false, false } };
+        list_append( 0, condition.raw, "ダブルクリックすると編集出来ます",
+                    "(この項目は削除して構いません)" );
+        condition.flag.regex = true;
+        list_append( 0, condition.raw, "^\\[(無断)?転載禁止\\]",
+                    "<span weight=\"heavy\" color=\"darkgreen\">...</span>" );
+        list_append( 0, condition.raw,
+                    "(\\[無?断?転載禁止\\]©([25]ch\\.net|bbspink\\.com)|\\[無?断?転載禁止\\]"
+                    "|©[25]ch\\.net|©bbspink\\.com)([[:blank:]]+\\[[[:digit:]]+\\])?$",
+                    "<span weight=\"heavy\" color=\"darkgreen\">...</span>\\3" );
+    }
+}
+
+
+ReplaceStr_Manager::~ReplaceStr_Manager()
+{
+    for( int i = 0; i < REPLACETARGET_MAX; ++i ) list_clear( i );
+}
+
+
+//
+// リストをクリア
+//
+void ReplaceStr_Manager::list_clear( const int id )
+{
+    ReplaceStrItemList::iterator it = m_list[ id ].begin();
+    for( ; it != m_list[ id ].end(); it++ ) delete *it;
+    m_list[ id ].clear();
+}
+
+
+//
+// アイテムをリストに追加
+//
+void ReplaceStr_Manager::list_append( const int id, const int condition, const std::string& pattern, const std::string& replace )
+{
+    if( id >= REPLACETARGET_MAX ) return;
+
+    ReplaceStrItem* item = new ReplaceStrItem;
+
+    item->condition = condition;
+    item->pattern = pattern;
+    item->replace = replace;
+
+    ReplaceStrCondition* cdt = ( ReplaceStrCondition* )&condition;
+    if( cdt->flag.regex ){
+        const bool newline = true;
+        const bool migemo = true;
+
+        if( ! item->creg.set( pattern, cdt->flag.icase, newline, migemo,
+                                cdt->flag.wchar, cdt->flag.norm ) ){
+            std::string msg ="invlid replacestr pattern: ";
+            msg += item->creg.errstr() + ": " + pattern;
+            MISC::ERRMSG( msg );
+        }
+    }
+
+#ifdef _DEBUG
+    std::cout << "conditnon=" << item->condition << " pattern=" << item->pattern
+              << " replace=" << item->replace << std::endl;
+#endif
+
+    m_list[ id ].push_back( item );
+}
+
+
+//
+// xml -> リスト
+//
+void ReplaceStr_Manager::xml2list( const std::string& xml )
+{
+    for( int i = 0; i < REPLACETARGET_MAX; ++i ) list_clear( i );
+    if( xml.empty() ) return;
+
+    XML::Document document( xml );
+
+    XML::Dom* root = document.get_root_element( std::string( ROOT_NODE_NAME_REPLACESTR ) );
+    if( ! root ) return;
+
+    XML::DomList domlist = root->childNodes();
+
+#ifdef _DEBUG
+    std::cout << "ReplaceStr_Manager::xml2list";
+    std::cout << " children =" << document.childNodes().size() << std::endl;
+#endif
+
+    std::list< XML::Dom* >::const_iterator it = domlist.begin();
+    for( ; it != domlist.end(); ++it ){
+
+        if( ( *it )->nodeType() != XML::NODE_TYPE_ELEMENT ) continue;
+
+        const int type = XML::get_type( ( *it )->nodeName() );
+        if( type != TYPE_DIR ) continue;
+
+        const size_t id = target_id( ( *it )->getAttribute( "name" ) );
+        if( id >= REPLACETARGET_MAX ) continue;
+
+        m_chref[ id ] = ( *it )->getAttribute( "chref" ) == "true";
+
+        XML::DomList domlist_query = ( *it )->childNodes();
+        std::list< XML::Dom* >::const_iterator it_query = domlist_query.begin();
+        for( ; it_query != domlist_query.end(); ++it_query ){
+
+            if( ( *it_query )->nodeType() != XML::NODE_TYPE_ELEMENT ) continue;
+
+            const int type = XML::get_type( ( *it_query )->nodeName() );
+            if( type != TYPE_REPLACESTR ) continue;
+
+            list_append( id, atoi( ( *it_query )->getAttribute( "condition" ).c_str() ),
+                            ( *it_query )->getAttribute( "pattern" ),
+                            ( *it_query )->getAttribute( "replace" ) );
+        }
+    }
+}
+
+
+//
+// XML 保存
+//
+void ReplaceStr_Manager::save_xml()
+{
+    XML::Document document;
+    XML::Dom* root = document.appendChild( XML::NODE_TYPE_ELEMENT, std::string( ROOT_NODE_NAME_REPLACESTR ) );
+    if( ! root ) return;
+
+    for( int i = 0; i < REPLACETARGET_MAX; ++i ){
+
+        if( m_list[ i ].empty() ) continue;
+
+        XML::Dom* node = dom_append( root, i, m_chref[ i ] );
+
+        ReplaceStrItemList::const_iterator it = m_list[ i ].begin();
+        for( ; it != m_list[ i ].end(); ++it ){
+            dom_append( node, ( *it )->condition, ( *it )->pattern, ( *it )->replace );
+        }
+    }
+
+#ifdef _DEBUG
+    std::cout << "ReplaceStr_Manager::save_xml" << std::endl;
+    std::cout << document.get_xml() << std::endl;
+#endif
+
+    CACHE::save_rawdata( CACHE::path_replacestr(), document.get_xml() );
+}
+
+
+//
+// 置換対象の要素名からid取得
+//
+int ReplaceStr_Manager::target_id( const std::string& name )
+{
+    int id;
+
+    for( id = 0; id < REPLACETARGET_MAX; ++id )
+        if( name.compare( 0, std::string::npos, target_str[ id ] ) == 0 ) break;
+
+    return id;
+}
+
+
+//
+// 置換対象idから要素名取得
+//
+std::string ReplaceStr_Manager::target_name( const int id )
+{
+    const char *name;
+
+    if( id >= REPLACETARGET_MAX ) name = NULL;
+    else name = target_str[ id ];
+
+    return std::string( name );
+}
+
+
+//
+// 実行
+//
+std::string ReplaceStr_Manager::replace( const char* str, const int lng, const int id )
+{
+    if( id >= REPLACETARGET_MAX || m_list[ id ].empty() ) return std::string( str, lng );
+
+    std::string str_work;
+
+    if( m_chref[ id ] ) str_work = MISC::chref_decode( str, lng, false );
+    else str_work.assign( str, lng );
+
+#ifdef _DEBUG
+    std::cout << "ReplaceStr_Manager::replace str=" << str_work << std::endl;
+#endif
+
+    JDLIB::Regex regex;
+
+    ReplaceStrItemList::const_iterator it = m_list[ id ].begin();
+    for( ; it != m_list[ id ].end(); ++it ){
+
+        ReplaceStrCondition cdt;
+        cdt.raw = ( *it )->condition;
+
+        if( ! cdt.flag.active || ( *it )->pattern.empty() ) continue;
+
+#ifdef _DEBUG
+        std::cout << "icase=" << cdt.flag.icase << " regex=" << cdt.flag.regex
+                  << " wchar=" << cdt.flag.wchar << " norm=" << cdt.flag.norm
+                  << " pattern=" << ( *it )->pattern
+                  << " replace=" << ( *it )->replace << std::endl;
+#endif
+        if( cdt.flag.regex ){
+            bool match = false;
+            int offset = 0;
+            const int lng_str = str_work.length();
+            std::string str_tmp;
+
+            // 文字列が空の時は"^$"などにマッチするように改行を追加
+            if( lng_str == 0 ) str_work.push_back( '\n' );
+
+            while( regex.match( ( *it )->creg, str_work, offset, match ) ){
+                match = true;
+
+                const int p0 = regex.pos( 0 );
+                if( p0 != offset ) str_tmp += str_work.substr( offset, p0 - offset );
+
+                // \0 ... \9 の文字列を置換
+                str_tmp += regex.replace( ( *it )->replace );
+
+                offset = p0 + regex.str( 0 ).length();
+                if( offset >= lng_str ) break;
+
+                // 0文字にマッチするパターンは繰り返さない
+                if( regex.str( 0 ).length() == 0 ) break;
+            }
+
+            if( match ){
+                if( lng_str > offset )
+                    str_work = str_tmp + str_work.substr( offset, lng_str - offset );
+                else
+                    str_work = str_tmp;
+            }
+        }
+
+        // 正規表現を使わない置換処理
+        else if( cdt.flag.icase ) str_work = MISC::replace_casestr( str_work, ( *it )->pattern, ( *it )->replace );
+        else str_work = MISC::replace_str( str_work, ( *it )->pattern, ( *it )->replace );
+    }
+
+#ifdef _DEBUG
+    std::cout << "replaced str=" << str_work << std::endl;
+#endif
+
+    return str_work;
+}
+
+
+//
+// 置換対象のXMLノード作成
+//
+XML::Dom* ReplaceStr_Manager::dom_append( XML::Dom* node, const int id, const bool chref )
+{
+    XML::Dom* dir = node->appendChild( XML::NODE_TYPE_ELEMENT, XML::get_name( TYPE_DIR ) );
+
+    dir->setAttribute( "name", target_name( id ) );
+    dir->setAttribute( "chref", chref ? "true" : "false" );
+
+    return dir;
+}
+
+
+//
+// 置換条件のXMLノード作成
+//
+XML::Dom* ReplaceStr_Manager::dom_append( XML::Dom* node, const int condition, const std::string& pattern, const std::string& replace )
+{
+    XML::Dom* query = node->appendChild( XML::NODE_TYPE_ELEMENT, XML::get_name( TYPE_REPLACESTR ) );
+
+    query->setAttribute( "condition", condition );
+    query->setAttribute( "pattern", pattern );
+    query->setAttribute( "replace", replace );
+
+    return query;
+}
diff -aurNZ jd-2.8.9-150226/src/replacestrmanager.h jd-2.8.9-a171004/src/replacestrmanager.h
--- jd-2.8.9-150226/src/replacestrmanager.h	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/replacestrmanager.h	2016-05-20 01:07:23.091493000 +0900
@@ -0,0 +1,98 @@
+// ライセンス: GPL2
+
+//
+// 文字列置換の管理クラス
+//
+
+#ifndef _REPLACESTRMANAGER_H
+#define _REPLACESTRMANAGER_H
+
+#include <string>
+#include <list>
+
+#include "jdlib/jdregex.h"
+
+namespace XML
+{
+    class Dom;
+}
+
+namespace CORE
+{
+    enum
+    {
+        REPLACETARGET_SUBJECT = 0,
+        REPLACETARGET_NAME,
+        REPLACETARGET_MAIL,
+        REPLACETARGET_DATE,
+        REPLACETARGET_MESSAGE,
+        REPLACETARGET_MAX
+    };
+
+    union ReplaceStrCondition
+    {
+        struct {
+            unsigned int active : 1;
+            unsigned int icase  : 1;
+            unsigned int regex  : 1;
+            unsigned int wchar  : 1;
+            unsigned int norm   : 1;
+            unsigned int : 27;
+        } flag;
+        int raw;
+    };
+
+    struct ReplaceStrItem
+    {
+        int condition;
+        std::string pattern;
+        std::string replace;
+        JDLIB::RegexPattern creg;
+    };
+
+    typedef std::list< ReplaceStrItem* > ReplaceStrItemList;
+
+    class ReplaceStr_Manager
+    {
+        ReplaceStrItemList m_list[ REPLACETARGET_MAX ];
+        bool m_chref[ REPLACETARGET_MAX ];
+
+    public:
+
+        ReplaceStr_Manager();
+        ~ReplaceStr_Manager();
+
+        ReplaceStrItemList::const_iterator list_begin( const int id ) const { return m_list[ id ].begin(); }
+        ReplaceStrItemList::const_iterator list_end( const int id ) const { return m_list[ id ].end(); }
+        size_t list_size( const int id ) const { return m_list[ id ].size(); }
+        void list_clear( const int id );
+        void list_append( const int id, const int condition, const std::string& pattern, const std::string& replace );
+
+        bool get_chref( const int id ) const { return m_chref[ id ]; }
+        void set_chref( const int id, const bool chref ){ m_chref[ id ] = chref; }
+
+        void save_xml();
+
+        // 文字列を置換
+        std::string replace( const char* str, const int lng, const int id );
+
+        static XML::Dom* dom_append( XML::Dom* node, const int id, const bool chref );
+        static XML::Dom* dom_append( XML::Dom* node, const int condition, const std::string& pattern, const std::string& replace );
+
+      private:
+
+        void xml2list( const std::string& xml );
+
+        static int target_id( const std::string& name );
+        static std::string target_name( const int id );
+    };
+
+    ///////////////////////////////////////
+    // インターフェース
+
+    ReplaceStr_Manager* get_replacestr_manager();
+    void delete_replacestr_manager();
+}
+
+
+#endif
diff -aurNZ jd-2.8.9-150226/src/replacestrpref.cpp jd-2.8.9-a171004/src/replacestrpref.cpp
--- jd-2.8.9-150226/src/replacestrpref.cpp	1970-01-01 09:00:00.000000000 +0900
+++ jd-2.8.9-a171004/src/replacestrpref.cpp	2016-05-23 23:01:05.082311000 +0900
@@ -0,0 +1,516 @@
+// ライセンス: GPL2
+
+//#define _DEBUG
+#include "jddebug.h"
+
+#include "replacestrmanager.h"
+#include "replacestrpref.h"
+
+#include "control/controlid.h"
+#include "jdlib/miscgtk.h"
+#include "jdlib/miscutil.h"
+#include "skeleton/msgdiag.h"
+#include "xml/document.h"
+
+#include "command.h"
+
+using namespace CORE;
+
+ReplaceStrDiag::ReplaceStrDiag( Gtk::Window* parent, const int condition, const std::string& pattern, const std::string& replace )
+    : SKELETON::PrefDiag( parent, "" )
+    , m_button_copy( "この設定をクリップボードにコピー" )
+    , m_check_active( "有効" )
+    , m_check_icase( "大文字小文字" )
+    , m_check_regex( "正規表現" )
+    , m_check_wchar( "全角半角" )
+    , m_check_norm( "互換文字" )
+    , m_entry_pattern( true, "置換パターン:" )
+    , m_entry_replace( true, "置換文字列:" )
+{
+    resize( 600, 1 );
+
+    m_button_copy.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrDiag::slot_copy ) );
+    m_check_regex.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrDiag::slot_sens ) );
+    m_check_wchar.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrDiag::slot_sens ) );
+    m_check_norm.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrDiag::slot_sens ) );
+
+    ReplaceStrCondition cdt;
+    cdt.raw = condition;
+
+    m_check_active.set_active( cdt.flag.active );
+    m_check_icase.set_active( cdt.flag.icase );
+    m_check_regex.set_active( cdt.flag.regex );
+    m_check_wchar.set_active( cdt.flag.wchar );
+    m_check_wchar.set_sensitive( cdt.flag.regex && !cdt.flag.norm );
+    m_check_norm.set_active( cdt.flag.norm );
+    m_check_norm.set_sensitive( cdt.flag.regex && cdt.flag.wchar );
+
+    m_check_active.set_tooltip_text( "この条件の置換を有効にする" );
+    m_check_icase.set_tooltip_text( "大文字小文字を区別しない" );
+    m_check_regex.set_tooltip_text( "正規表現を使用する" );
+    m_check_wchar.set_tooltip_text( "英数字とカナの種類(俗にいう全角半角)を区別しない" );
+    m_check_norm.set_tooltip_text( "Unicodeの互換文字を区別しない" );
+
+    m_hbox_regex.pack_start( m_check_icase, Gtk::PACK_SHRINK );
+    m_hbox_regex.pack_start( m_check_regex, Gtk::PACK_SHRINK );
+    m_hbox_regex.pack_start( m_check_wchar, Gtk::PACK_SHRINK );
+    m_hbox_regex.pack_start( m_check_norm, Gtk::PACK_SHRINK );
+
+    m_entry_pattern.set_text( pattern );
+    m_entry_replace.set_text( replace );
+
+    m_hbox_active.pack_start( m_check_active );
+    m_hbox_active.pack_start( m_button_copy, Gtk::PACK_SHRINK );
+
+    get_vbox()->set_spacing( 8 );
+    get_vbox()->pack_start( m_hbox_active, Gtk::PACK_SHRINK );
+    get_vbox()->pack_start( m_hbox_regex, Gtk::PACK_SHRINK );
+    get_vbox()->pack_start( m_entry_pattern, Gtk::PACK_SHRINK );
+    get_vbox()->pack_start( m_entry_replace, Gtk::PACK_SHRINK );
+
+    set_title( "置換条件設定" );
+    show_all_children();
+}
+
+
+//
+// 条件フラグ取得
+//
+int ReplaceStrDiag::get_condition() const
+{
+    ReplaceStrCondition condition;
+
+    condition.raw = 0;
+    condition.flag.active = get_active();
+    condition.flag.icase = get_icase();
+    condition.flag.regex = get_regex();
+    condition.flag.wchar = get_wchar();
+    condition.flag.norm = get_norm();
+
+    return condition.raw;
+}
+
+
+//
+// クリップボードにコピー
+//
+void ReplaceStrDiag::slot_copy()
+{
+    XML::Document document;
+    XML::Dom* node = ReplaceStr_Manager::dom_append( &document, get_condition(), get_pattern(), get_replace() );
+
+    MISC::CopyClipboard( node->get_xml() );
+}
+
+
+//
+// wcharチェックボックスのsensitive切り替え
+//
+void ReplaceStrDiag::slot_sens()
+{
+    m_check_wchar.set_sensitive( get_regex() && !get_norm() );
+    m_check_norm.set_sensitive( get_regex() && get_wchar() );
+}
+
+
+///////////////////////////////////////////////
+
+static const char * const target_name[ REPLACEPREF_NUM_TARGET ] = {
+    "スレタイトル", "名前", "メール", "日付/ID", "本文" };
+
+ReplaceStrPref::ReplaceStrPref( Gtk::Window* parent, const std::string& url )
+    : SKELETON::PrefDiag( parent, url ),
+      m_id_target( 0 ),
+      m_label_target( "置換対象:" ),
+      m_label_menu( target_name[ 0 ] ),
+      m_menu_target( true, m_label_menu ),
+      m_check_chref( "対象の置換前に文字参照をデコード" ),
+      m_button_top( Gtk::Stock::GOTO_TOP ),
+      m_button_up( Gtk::Stock::GO_UP ),
+      m_button_down( Gtk::Stock::GO_DOWN ),
+      m_button_bottom( Gtk::Stock::GOTO_BOTTOM ),
+      m_button_delete( Gtk::Stock::DELETE ),
+      m_button_add( Gtk::Stock::ADD )
+{
+    m_button_top.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_top ) );
+    m_button_up.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_up ) );
+    m_button_down.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_down ) );
+    m_button_bottom.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_bottom ) );
+    m_button_delete.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_delete ) );
+    m_button_add.signal_clicked().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_add ) );
+
+    for( int i = 0; i < REPLACEPREF_NUM_TARGET; ++i ){
+        m_store[ i ] = Gtk::ListStore::create( m_columns );
+    }
+    m_current_store = m_store[ 0 ];
+    m_treeview.set_model( m_current_store );
+    m_treeview.set_size_request( 640, 400 );
+    m_treeview.signal_row_activated().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_row_activated ) );
+    m_treeview.sig_key_release().connect( sigc::mem_fun(*this, &ReplaceStrPref::slot_key_release ) );
+
+    Gtk::TreeViewColumn* column[ REPLACEPREF_NUM_COLUMNS ];
+
+    column[ 0 ] = Gtk::manage( new Gtk::TreeViewColumn( "有効", m_columns.m_col_active ) );
+    column[ 0 ]->set_fixed_width( 35 );
+    column[ 0 ]->set_alignment( Gtk::ALIGN_CENTER );
+    column[ 1 ] = Gtk::manage( new Gtk::TreeViewColumn( "大小", m_columns.m_col_icase ) );
+    column[ 1 ]->set_fixed_width( 35 );
+    column[ 1 ]->set_alignment( Gtk::ALIGN_CENTER );
+    column[ 2 ] = Gtk::manage( new Gtk::TreeViewColumn( "正規", m_columns.m_col_regex ) );
+    column[ 2 ]->set_fixed_width( 35 );
+    column[ 2 ]->set_alignment( Gtk::ALIGN_CENTER );
+    column[ 3 ] = Gtk::manage( new Gtk::TreeViewColumn( "置換パターン", m_columns.m_col_pattern ) );
+    column[ 3 ]->set_fixed_width( 220 );
+    column[ 4 ] = Gtk::manage( new Gtk::TreeViewColumn( "置換文字列", m_columns.m_col_replace ) );
+    column[ 4 ]->set_fixed_width( 200 );
+
+    for( int i = 0; i < REPLACEPREF_NUM_COLUMNS; ++i ){
+        column[ i ]->set_sizing( Gtk::TREE_VIEW_COLUMN_FIXED );
+        column[ i ]->set_resizable( true );
+        m_treeview.append_column( *( column[ i ] ) );
+    }
+
+    m_scrollwin.add( m_treeview );
+    m_scrollwin.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS );
+
+    m_vbuttonbox.pack_start( m_button_top, Gtk::PACK_SHRINK );
+    m_vbuttonbox.pack_start( m_button_up, Gtk::PACK_SHRINK );
+    m_vbuttonbox.pack_start( m_button_down, Gtk::PACK_SHRINK );
+    m_vbuttonbox.pack_start( m_button_bottom, Gtk::PACK_SHRINK );
+    m_vbuttonbox.pack_start( m_button_delete, Gtk::PACK_SHRINK );
+    m_vbuttonbox.pack_start( m_button_add, Gtk::PACK_SHRINK );
+    m_vbuttonbox.set_layout( Gtk::BUTTONBOX_START );
+    m_vbuttonbox.set_spacing( 4 );
+
+    m_hbox.pack_start( m_scrollwin, Gtk::PACK_SHRINK );
+    m_hbox.pack_start( m_vbuttonbox, Gtk::PACK_SHRINK );
+
+    std::vector< std::string > list_target;
+    for( int i = 0; i < REPLACEPREF_NUM_TARGET; ++i ) list_target.push_back( target_name[ i ] );
+    m_menu_target.append_menu( list_target );
+    m_menu_target.set_enable_sig_clicked( false );
+    m_menu_target.signal_selected().connect( sigc::mem_fun( *this, &ReplaceStrPref::slot_target_changed ) );
+
+    m_hbox_target.set_border_width( 8 );
+    m_hbox_target.set_spacing( 8 );
+    m_hbox_target.pack_start( m_label_target, Gtk::PACK_SHRINK );
+    m_hbox_target.pack_start( m_menu_target, Gtk::PACK_SHRINK );
+
+    m_check_chref.set_tooltip_markup( "ダブルクォーテション<b>\"</b>, アンパサント"
+            "<b>&amp;</b>, 少なり記号<b>&lt;</b>, 大なり記号<b>&gt;</b>を<b>除く</b>"
+            "文字参照をデコードしてから置換を行います。" );
+
+    get_vbox()->set_spacing( 8 );
+    get_vbox()->pack_start( m_hbox_target, Gtk::PACK_SHRINK );
+    get_vbox()->pack_start( m_check_chref, Gtk::PACK_SHRINK );
+    get_vbox()->pack_start( m_hbox );
+
+    show_all_children();
+    set_title( "文字列置換設定" );
+
+    append_rows();
+}
+
+
+void ReplaceStrPref::append_rows()
+{
+    ReplaceStr_Manager* mgr = CORE::get_replacestr_manager();
+
+    for( int i = 0; i < REPLACEPREF_NUM_TARGET; ++i ){
+
+        m_chref[ i ] = mgr->get_chref( i );
+
+        ReplaceStrItemList::const_iterator it = mgr->list_begin( i );
+
+        for( ; it != mgr->list_end( i ); ++it ){
+            append_row( m_store[ i ], ( *it )->condition, ( *it )->pattern, ( *it )->replace );
+        }
+    }
+
+    m_check_chref.set_active( m_chref[ 0 ] );
+    select_row( get_top_row() );
+}
+
+
+void ReplaceStrPref::append_row( Glib::RefPtr< Gtk::ListStore > store, const int condition, const std::string& pattern, const std::string& replace )
+{
+    Gtk::TreeModel::Row row = *( store->append() );
+
+    if( row ){
+        const ReplaceStrCondition* cdt = ( const ReplaceStrCondition * ) &condition;
+
+        row[ m_columns.m_col_active ] = cdt->flag.active;
+        row[ m_columns.m_col_icase ] = cdt->flag.icase;
+        row[ m_columns.m_col_regex ] = cdt->flag.regex;
+        row[ m_columns.m_col_wchar ] = cdt->flag.wchar;
+        row[ m_columns.m_col_norm ] = cdt->flag.norm;
+        row[ m_columns.m_col_pattern ] = pattern;
+        row[ m_columns.m_col_replace ] = replace;
+
+        select_row( row );
+    }
+}
+
+
+Gtk::TreeModel::const_iterator ReplaceStrPref::get_selected_row() const
+{
+    std::list< Gtk::TreeModel::Path > paths = m_treeview.get_selection()->get_selected_rows();
+
+    if( ! paths.size() ) return Gtk::TreeModel::const_iterator();
+    return *( m_current_store->get_iter( *paths.begin() ) );
+}
+
+
+Gtk::TreeModel::const_iterator ReplaceStrPref::get_top_row() const
+{
+    Gtk::TreeModel::Children children = m_current_store->children();
+
+    if( children.empty() ) return Gtk::TreeModel::const_iterator();
+    return children.begin();
+}
+
+
+Gtk::TreeModel::const_iterator ReplaceStrPref::get_bottom_row() const
+{
+    Gtk::TreeModel::Children children = m_current_store->children();
+
+    if( children.empty() ) return Gtk::TreeModel::const_iterator();
+    return --children.end();
+}
+
+
+void ReplaceStrPref::select_row( const Gtk::TreeModel::const_iterator& it )
+{
+    if( !it ) return;
+
+    const Gtk::TreePath path( it );
+    m_treeview.get_selection()->select( path );
+}
+
+
+//
+// OK ボタンを押した
+//
+void ReplaceStrPref::slot_ok_clicked()
+{
+#ifdef _DEBUG
+    std::cout << "ReplaceStrPref::slot_ok_clicked" << std::endl;
+#endif
+
+    ReplaceStr_Manager* mgr = CORE::get_replacestr_manager();
+
+    m_chref[ m_id_target ] = m_check_chref.get_active();
+
+    for( int i = 0; i < REPLACEPREF_NUM_TARGET; ++i ){
+
+        mgr->list_clear( i );
+
+        mgr->set_chref( i, m_chref[ i ] );
+
+        Gtk::TreeModel::Children children = m_store[ i ]->children();
+        for( Gtk::TreeModel::iterator it = children.begin(); it != children.end(); ++it ){
+
+            Gtk::TreeModel::Row row = ( *it );
+
+            const bool active = row[ m_columns.m_col_active ];
+            const bool icase = row[ m_columns.m_col_icase ];
+            const bool regex = row[ m_columns.m_col_regex ];
+            const bool wchar = row[ m_columns.m_col_wchar ];
+            const bool norm = row[ m_columns.m_col_norm ];
+
+            ReplaceStrCondition cdt = { { active, icase, regex, wchar, norm } };
+            const std::string& pattern = row[ m_columns.m_col_pattern ];
+            const std::string& replace = row[ m_columns.m_col_replace ];
+
+            mgr->list_append( i, cdt.raw, pattern, replace );
+#ifdef _DEBUG
+            std::cout << "target=" << i << " condition=" << cdt.raw
+                << " pattern=" << pattern << " replace=" << replace << std::endl;
+#endif
+        }
+    }
+
+    CORE::get_replac