From 716335785234f27347be4362ae72b2c067d79b18 Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Thu, 21 Mar 2019 10:39:09 +0900 Subject: [PATCH 1/7] Revert "Merge pull request #792 from berryzplus/feature/workaround_for_issue398" This reverts commit a722f9c86f2860606b3f9e7dc79bb169294cfaea, reversing changes made to 8087f26f28709c62fbb51c52619c29c2475df269. --- sakura_core/parse/CWordParse.cpp | 199 --------- sakura_core/parse/CWordParse.h | 13 - sakura_core/util/string_ex.cpp | 415 ++++++++++++++++++ sakura_core/util/string_ex.h | 6 + sakura_core/view/CEditView.cpp | 1 + sakura_core/view/CEditView_Mouse.cpp | 1 + sakura_core/view/colors/CColor_Url.cpp | 3 +- .../unittests/code-IsMailAddress_20160427.cpp | 76 ++++ tests/unittests/test-is_mailaddress.cpp | 187 ++++++++ 9 files changed, 688 insertions(+), 213 deletions(-) create mode 100644 tests/unittests/code-IsMailAddress_20160427.cpp create mode 100644 tests/unittests/test-is_mailaddress.cpp diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 51684cad47..a538fb3202 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -308,202 +308,3 @@ bool CWordParse::SearchNextWordPosition4KW( } return false; } - -//! wcがasciiなら0-127のまま返す。それ以外は0を返す。 -uchar_t wc_to_c(wchar_t wc) -{ -#if 0 -//! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 - char buf[3]={0,0,0}; - int ret=wctomb(buf,wc); - if(ret==-1)return 0; //エラー - if(buf[1]!=0)return 0; //エラー扱い - return buf[0] <= 0x7F ? buf[0]: 0; //1バイトで表せたので、これを返す 2011.12.17 バッファオーバーランの修正 -#endif - // 2011.12.15 wctombを使わない版 - if(wc <= 0x7F){ - return (uchar_t)wc; - } - return 0; -} - -//@@@ 2002.01.24 Start by MIK -/*! - 文字列がURLかどうかを検査する。 - - @retval TRUE URLである - @retval FALSE URLでない - - @note 関数内に定義したテーブルは必ず static const 宣言にすること(性能に影響します)。 - url_char の値は url_table の配列番号+1 になっています。 - 新しい URL を追加する場合は #define 値を修正してください。 - url_table は頭文字がアルファベット順になるように並べてください。 - - 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 -*/ -BOOL IsURL( - const wchar_t* pszLine, //!< [in] 文字列 - int offset, //!< [in] 検査を開始する位置。 - int nLineLen, //!< [in] 文字列の長さ - int* pnMatchLen //!< [out] URLの長さ。offset からの距離。 -) -{ - struct _url_table_t { - wchar_t name[12]; - int length; - bool is_mail; - }; - static const struct _url_table_t url_table[] = { - /* アルファベット順 */ - { L"file://", 7, false }, /* 1 */ - { L"ftp://", 6, false }, /* 2 */ - { L"gopher://", 9, false }, /* 3 */ - { L"http://", 7, false }, /* 4 */ - { L"https://", 8, false }, /* 5 */ - { L"mailto:", 7, true }, /* 6 */ - { L"news:", 5, false }, /* 7 */ - { L"nntp://", 7, false }, /* 8 */ - { L"prospero://", 11, false }, /* 9 */ - { L"telnet://", 9, false }, /* 10 */ - { L"tp://", 5, false }, /* 11 */ //2004.02.02 - { L"ttp://", 6, false }, /* 12 */ //2004.02.02 - { L"wais://", 7, false }, /* 13 */ - { L"{", 0, false } /* 14 */ /* '{' is 'z'+1 : terminate */ - }; - -/* テーブルの保守性を高めるための定義 */ - const char urF = 1; - const char urG = 3; - const char urH = 4; - const char urM = 6; - const char urN = 7; - const char urP = 9; - const char urT = 10; - const char urW = 13; //2004.02.02 - - static const char url_char[] = { - /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +00: */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +10: */ - 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, /* +20: " !"#$%&'()*+,-./" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, /* +30: "0123456789:;<=>?" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* +40: "@ABCDEFGHIJKLMNO" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, -1, /* +50: "PQRSTUVWXYZ[\]^_" */ - 0, -1, -1, -1, -1, -1,urF,urG,urH, -1, -1, -1, -1,urM,urN, -1, /* +60: "`abcdefghijklmno" */ - urP, -1, -1, -1,urT, -1, -1,urW, -1, -1, -1, 0, 0, 0, -1, 0, /* +70: "pqrstuvwxyz{|}~ " */ - /* あと128バイト犠牲にすればif文を2箇所削除できる */ - /* 0 : not url char - * -1 : url char - * other: url head char --> url_table array number + 1 - */ - }; - - const wchar_t * const begin = pszLine + offset; - const wchar_t * const end = pszLine + nLineLen; - const struct _url_table_t *urlp; - int i; - - if( wc_to_c(*begin)==0 ) return FALSE; /* 2バイト文字 */ - if( 0 < url_char[wc_to_c(*begin)] ){ /* URL開始文字 */ - for(urlp = &url_table[url_char[wc_to_c(*begin)]-1]; urlp->name[0] == wc_to_c(*begin); urlp++){ /* URLテーブルを探索 */ - if( (urlp->length <= end - begin) && (auto_memcmp(urlp->name, begin, urlp->length) == 0) ){ /* URLヘッダは一致した */ - if( urlp->is_mail ){ /* メール専用の解析へ */ - if( IsMailAddress(begin, urlp->length, end - begin - urlp->length, pnMatchLen) ){ - *pnMatchLen = *pnMatchLen + urlp->length; - return TRUE; - } - return FALSE; - } - for(i = urlp->length; i < end - begin; i++){ /* 通常の解析へ */ - if( wc_to_c(begin[i])==0 || (!(url_char[wc_to_c(begin[i])])) ) break; /* 終端に達した */ - } - if( i == urlp->length ) return FALSE; /* URLヘッダだけ */ - *pnMatchLen = i; - return TRUE; - } - } - } - return IsMailAddress(pszLine, offset, nLineLen, pnMatchLen); -} - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す - @date 2016.04.27 記号類を許可 -*/ -BOOL IsMailAddress( const wchar_t* pszBuf, int offset, int nBufLen, int* pnAddressLenfth ) -{ - struct { - bool operator()(const wchar_t ch) - { - return 0x21 <= ch && ch <= 0x7E && NULL == wcschr(L"\"(),:;<>@[\\]", ch); - } - } IsValidChar; - -/* - 直前の文字を利用した境界判定 -*/ - if (0 < offset && IsValidChar(pszBuf[offset-1])) { - return FALSE; - } - - pszBuf += offset; - nBufLen -= offset; - offset = 0; - - int j; - int nDotCount; - int nBgn; - - - j = 0; - if(pszBuf[j] != L'.' && IsValidChar(pszBuf[j])){ - j++; - }else{ - return FALSE; - } - while( j < nBufLen - 2 && IsValidChar(pszBuf[j]) ){ - j++; - } - if( j == 0 || j >= nBufLen - 2 ){ - return FALSE; - } - if( L'@' != pszBuf[j] ){ - return FALSE; - } -// nAtPos = j; - j++; - nDotCount = 0; -// nAlphaCount = 0; - - - for (;;) { - nBgn = j; - while( j < nBufLen && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'-') - || (pszBuf[j] == L'_') - ) - ){ - j++; - } - if( 0 == j - nBgn ){ - return FALSE; - } - if( L'.' != pszBuf[j] ){ - if( 0 == nDotCount ){ - return FALSE; - }else{ - break; - } - }else{ - nDotCount++; - j++; - } - } - if( NULL != pnAddressLenfth ){ - *pnAddressLenfth = j; - } - return TRUE; -} diff --git a/sakura_core/parse/CWordParse.h b/sakura_core/parse/CWordParse.h index ba59fd6521..315e553025 100644 --- a/sakura_core/parse/CWordParse.h +++ b/sakura_core/parse/CWordParse.h @@ -125,19 +125,6 @@ class CWordParse{ static bool _match_charlist( const WCHAR c, const WCHAR *pszList ); }; -BOOL IsURL( const wchar_t* psz, int offset, int length, int* outLength);/* offset 引数の追加により境界判定が行える高速版 */ -inline -BOOL IsURL( const wchar_t* psz, int length, int* outLength) /* 指定アドレスがURLの先頭ならばTRUEとその長さを返す。高速版の追加により obsolete. */ -{ - return IsURL(psz, 0, length, outLength); -} -BOOL IsMailAddress( const wchar_t* psz, int offset, int length, int* outLength); /* offset 引数の追加により境界判定が行える高速版 */ -inline -BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength) /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ -{ - return IsMailAddress(psz, 0, length, outLength); -} - // ACHAR 版 inline bool CWordParse::_match_charlist( const ACHAR c, const ACHAR *pszList ) { diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index da7b86b34e..ba592117f3 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -871,3 +871,418 @@ int skr_towlower( int c ) #endif return towlower( (wchar_t)c ); } + +//! wcがasciiなら0-127のまま返す。それ以外は0を返す。 +inline static uchar_t wc_to_c(wchar_t wc) +{ +#if 0 +//! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 + char buf[3]={0,0,0}; + int ret=wctomb(buf,wc); + if(ret==-1)return 0; //エラー + if(buf[1]!=0)return 0; //エラー扱い + return buf[0] <= 0x7F ? buf[0]: 0; //1バイトで表せたので、これを返す 2011.12.17 バッファオーバーランの修正 +#endif + // 2011.12.15 wctombを使わない版 + if(wc <= 0x7F){ + return (uchar_t)wc; + } + return 0; +} + +/*! + 文字列がURLかどうかを検査する。 + + @retval TRUE URLである + @retval FALSE URLでない + + @note 関数内に定義したテーブルは必ず static const 宣言にすること(性能に影響します)。 + url_char の値は url_table の配列番号+1 になっています。 + 新しい URL を追加する場合は #define 値を修正してください。 + url_table は頭文字がアルファベット順になるように並べてください。 + + 2002.01.24 MIK + 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 +*/ +BOOL IsURL( + const wchar_t* pszLine, //!< [in] 文字列 + int nLineLen, //!< [in] 文字列の長さ + int* pnMatchLen //!< [out] URLの長さ +) +{ + // TODO: この関数は、UNCアドレスも含めて見直した方がよさげ by berryzplus + struct _url_table_t { + wchar_t name[12]; + int length; + bool is_mail; + }; + static const struct _url_table_t url_table[] = { + /* アルファベット順 */ + { L"file://", 7, false }, /* 1 */ + { L"ftp://", 6, false }, /* 2 */ + { L"gopher://", 9, false }, /* 3 */ + { L"http://", 7, false }, /* 4 */ + { L"https://", 8, false }, /* 5 */ + { L"mailto:", 7, true }, /* 6 */ + { L"news:", 5, false }, /* 7 */ + { L"nntp://", 7, false }, /* 8 */ + { L"prospero://", 11, false }, /* 9 */ + { L"telnet://", 9, false }, /* 10 */ + { L"tp://", 5, false }, /* 11 */ //2004.02.02 + { L"ttp://", 6, false }, /* 12 */ //2004.02.02 + { L"wais://", 7, false }, /* 13 */ + { L"{", 0, false } /* 14 */ /* '{' is 'z'+1 : terminate */ + }; + +/* テーブルの保守性を高めるための定義 */ + constexpr char urF = 1; + constexpr char urG = 3; + constexpr char urH = 4; + constexpr char urM = 6; + constexpr char urN = 7; + constexpr char urP = 9; + constexpr char urT = 10; + constexpr char urW = 13; //2004.02.02 + + static const char url_char[] = { + /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +00: */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +10: */ + 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, /* +20: " !"#$%&'()*+,-./" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, /* +30: "0123456789:;<=>?" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* +40: "@ABCDEFGHIJKLMNO" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, -1, /* +50: "PQRSTUVWXYZ[\]^_" */ + 0, -1, -1, -1, -1, -1,urF,urG,urH, -1, -1, -1, -1,urM,urN, -1, /* +60: "`abcdefghijklmno" */ + urP, -1, -1, -1,urT, -1, -1,urW, -1, -1, -1, 0, 0, 0, -1, 0, /* +70: "pqrstuvwxyz{|}~ " */ + /* あと128バイト犠牲にすればif文を2箇所削除できる */ + /* 0 : not url char + * -1 : url char + * other: url head char --> url_table array number + 1 + */ + }; + + const wchar_t *p = pszLine; + const struct _url_table_t *urlp; + int i; + + if( wc_to_c(*p)==0 ) return FALSE; /* 2バイト文字 */ + if( 0 < url_char[wc_to_c(*p)] ){ /* URL開始文字 */ + for(urlp = &url_table[url_char[wc_to_c(*p)]-1]; urlp->name[0] == wc_to_c(*p); urlp++){ /* URLテーブルを探索 */ + if( (urlp->length <= nLineLen) && (auto_memcmp(urlp->name, pszLine, urlp->length) == 0) ){ /* URLヘッダは一致した */ + p += urlp->length; /* URLヘッダ分をスキップする */ + if( urlp->is_mail ){ /* メール専用の解析へ */ + if( IsMailAddress(p, nLineLen - urlp->length, pnMatchLen) ){ + *pnMatchLen = *pnMatchLen + urlp->length; + return TRUE; + } + return FALSE; + } + for(i = urlp->length; i < nLineLen; i++, p++){ /* 通常の解析へ */ + if( wc_to_c(*p)==0 || (!(url_char[wc_to_c(*p)])) ) break; /* 終端に達した */ + } + if( i == urlp->length ) return FALSE; /* URLヘッダだけ */ + *pnMatchLen = i; + return TRUE; + } + } + } + return IsMailAddress(pszLine, nLineLen, pnMatchLen); // TODO: FALSE じゃなくて? + // この関数の作成目的は「指定した文字列がURLかどうかを判定すること」だったと推定される。 + // 先行する「mailto:」のないメールアドレスはURLではないのでここはFALSEを返すべき。 + // 「URLとメールアドレスにリンクを付けたい」を叶えるための措置と考えられるが、もっといい解決策がありそうに思う。 + // とはいえ、すぐに着手できそうな状況ではないので、備忘目的でコメントだけ残しておく。 by berryzplus +} + +// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept; + +// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept; + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 + @date 2018.09.09 RFC準拠 +*/ +BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) +{ + // RFC5321による mailbox の最大文字数 + constexpr ptrdiff_t MAX_MAILBOX = 255; //255オクテット + + // mailboxの最小文字数(これより短いと構成要素を含めなくなる) + // 例) a@z.jp + // 1(@手前) + 1(@) + 1(ドメイン) + 1(.) + 2(TLD/ccTLD) = 6 + constexpr ptrdiff_t MIN_MAILBOX = 6; + + // 想定しないパラメータは前半チェックの前に弾く + if (pszBuf == nullptr || nBufLen < MIN_MAILBOX) return FALSE; + + // メールアドレスには必ず@が含まれる + const wchar_t* pszAtmark; + + // メールアドレス前半部分(@の手前)をチェックする + if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { + return FALSE; + } + + // メールアドレスの終了位置を受け取るポインタを宣言する + const wchar_t* pszEndOfMailBox; + + // メールアドレス後半部分(@の後ろ)をチェックする + if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) + { + return FALSE; + } + + // 全体の長さが制限を超えていないかチェックする + const auto cchAddressLength = pszEndOfMailBox - pszBuf; + if (MAX_MAILBOX < cchAddressLength) + { + return FALSE; // 文字数オーバー + } + + if (pnAddressLength != nullptr) + { + *pnAddressLength = cchAddressLength; + } + return TRUE; +} + +/*! + * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する + * + * 高速化のため単純化した条件でチェックしている + * 参照する標準は RFC5321 + * @see http://srgia.com/docs/rfc5321j.html + */ +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept +{ + // RFC5321による local-part の最大文字数 + constexpr ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット + + // 関数仕様 + assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない + assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない + + // 出力値を初期化する + *ppszAtmark = nullptr; + + // 文字列が二重引用符で始まっているかチェックして結果を保存 + const bool quoted = (L'"' == *pszStart); + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszStart + (quoted ? 1 : 0); + auto pszScanEnd = std::min(pszStart + MAX_LOCAL_PART + 1, pszEnd); + + // スキャン位置が終端に達するまでループ + while (pszScan < pszScanEnd) + { + switch (*pszScan) + { + case L'@': + if (pszStart == pszScan) + { + return false; // local-partは1文字以上なのでNG + } + if (quoted) + { + return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG + } + *ppszAtmark = pszScan; + return true; // ここが正常終了 + case L'\\': // エスケープ記号 + if (pszScan + 1 == pszScanEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) + { + return false; + } + pszScan++; // エスケープ記号の分1文字進める + break; + case L'"': // 二重引用符 + if (quoted && pszScan + 1 < pszScanEnd && L'@' == pszScan[1]) + { + *ppszAtmark = &pszScan[1]; + return true; // ここは準正常終了。正常終了とはあえて区別しない。 + } + return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 + } + pszScan++; + } + return false; // 文字数オーバー +} + +/*! + * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する + */ +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept +{ + // ccTLDの最小文字数 + constexpr ptrdiff_t MIN_TLD = 2; + + // ドメインの最小文字数 + constexpr ptrdiff_t MIN_DOMAIN = 3; + + // ドメインの最大文字数 + constexpr ptrdiff_t MAX_DOMAIN = 63; + + // 関数仕様 + assert(pszAtmark < pszEnd); // @位置と終了位置は逆転してはならない + assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない + + // 出力値を初期化する + *ppszEndOfMailBox = nullptr; + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszAtmark + 1; + if (pszScan == pszEnd) + { + return false; // @の後ろが0文字、長さが足りない + } + + auto dotCount = 0; + auto domainLength = 0; + auto prevHyphen = false; + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'.': // ドット記号 + if (dotCount == 0 && domainLength < MIN_DOMAIN) + { + return false; // ドメイン名の最小文字数は3なのでNG + } + if (0 < dotCount && domainLength < MIN_TLD) + { + // これはco.jpなどを正しく認識させるために必要。 + return false; // ドットで区切られる部分の最小文字数は2なのでNG + } + if (prevHyphen) + { + return false; // ハイフンに続くドットはNG + } + dotCount++; + domainLength = 0; + prevHyphen = false; + break; + case L'-': // ハイフン記号 + if (domainLength == 0) + { + return false; // ドットに続くハイフンはNG + } + if (prevHyphen) + { + return false; // 連続するハイフンはNG + } + domainLength++; + prevHyphen = true; + break; + default: + if (dotCount == 0) + { + return false; // ドメイン部には一つ以上のドット記号が必要なのでNG + } + if (domainLength == 0) + { + return false; // ドットで終わるドメインはNG + } + if (prevHyphen) + { + return false; // ハイフンで終わるドメインはNG + } + *ppszEndOfMailBox = pszScan; + return true; // ここも正常終了 + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + case L'A': + case L'B': + case L'C': + case L'D': + case L'E': + case L'F': + case L'G': + case L'H': + case L'I': + case L'J': + case L'K': + case L'L': + case L'M': + case L'N': + case L'O': + case L'P': + case L'Q': + case L'R': + case L'S': + case L'T': + case L'U': + case L'V': + case L'W': + case L'X': + case L'Y': + case L'Z': + case L'a': + case L'b': + case L'c': + case L'd': + case L'e': + case L'f': + case L'g': + case L'h': + case L'i': + case L'j': + case L'k': + case L'l': + case L'm': + case L'n': + case L'o': + case L'p': + case L'q': + case L'r': + case L's': + case L't': + case L'u': + case L'v': + case L'w': + case L'x': + case L'y': + case L'z': + domainLength++; + prevHyphen = false; + break; + } + pszScan++; + if (MAX_DOMAIN < domainLength) + { + return false; // 文字数オーバー + } + } + if (pszScan == pszEnd) + { + *ppszEndOfMailBox = pszScan; + return true; // ここが正常終了 + } + return false; +} diff --git a/sakura_core/util/string_ex.h b/sakura_core/util/string_ex.h index cbb515da92..a0a19acbcb 100644 --- a/sakura_core/util/string_ex.h +++ b/sakura_core/util/string_ex.h @@ -328,5 +328,11 @@ inline int wcsncmp_auto(const wchar_t* strData1, const wchar_t* szData2) #define _tcsncmp_literal strncmp_literal #endif +/* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ +BOOL IsURL(const wchar_t* pszLine, int nLineLen, int* pnMatchLen); + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す */ +BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLength); + #endif /* SAKURA_STRING_EX_29EB1DD7_7259_4D6C_A651_B9174E5C3D3C9_H_ */ /*[EOF]*/ diff --git a/sakura_core/view/CEditView.cpp b/sakura_core/view/CEditView.cpp index a50b2b586e..e9605686b2 100644 --- a/sakura_core/view/CEditView.cpp +++ b/sakura_core/view/CEditView.cpp @@ -55,6 +55,7 @@ #include "types/CTypeSupport.h" #include "convert/CConvert.h" #include "util/RegKey.h" +#include "util/string_ex.h" // IsURL #include "util/string_ex2.h" #include "util/os.h" //WM_MOUSEWHEEL,IMR_RECONVERTSTRING,WM_XBUTTON*,IMR_CONFIRMRECONVERTSTRING #include "util/module.h" diff --git a/sakura_core/view/CEditView_Mouse.cpp b/sakura_core/view/CEditView_Mouse.cpp index 196223eda9..9ce8818aae 100644 --- a/sakura_core/view/CEditView_Mouse.cpp +++ b/sakura_core/view/CEditView_Mouse.cpp @@ -36,6 +36,7 @@ #include "uiparts/CWaitCursor.h" #include "uiparts/HandCursor.h" #include "util/input.h" +#include "util/string_ex.h" // IsMailAddress #include "util/os.h" #include "sakura_rc.h" diff --git a/sakura_core/view/colors/CColor_Url.cpp b/sakura_core/view/colors/CColor_Url.cpp index 03d6c04de2..43f17daec0 100644 --- a/sakura_core/view/colors/CColor_Url.cpp +++ b/sakura_core/view/colors/CColor_Url.cpp @@ -6,6 +6,7 @@ #include "doc/CEditDoc.h" #include "doc/layout/CLayout.h" #include "types/CTypeSupport.h" +#include "util/string_ex.h" // IsURL // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // // URL // @@ -18,7 +19,7 @@ bool CColor_Url::BeginColor(const CStringRef& cStr, int nPos) int nUrlLen; if( _IsPosKeywordHead(cStr,nPos) /* URLを表示する */ - && IsURL( cStr.GetPtr(), nPos, cStr.GetLength(), &nUrlLen ) /* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ + && IsURL( cStr.GetPtr() + nPos, cStr.GetLength() - nPos, &nUrlLen ) /* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ ){ this->m_nCOMMENTEND = nPos + nUrlLen; return true; diff --git a/tests/unittests/code-IsMailAddress_20160427.cpp b/tests/unittests/code-IsMailAddress_20160427.cpp new file mode 100644 index 0000000000..926c6936f1 --- /dev/null +++ b/tests/unittests/code-IsMailAddress_20160427.cpp @@ -0,0 +1,76 @@ +// テスト対象の旧関数(コピペで埋め込み) +#include "StdAfx.h" + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 +*/ +BOOL IsMailAddress_20160427( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) +{ + int j; + int nDotCount; + int nBgn; + + j = 0; + if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + ){ + j++; + }else{ + return FALSE; + } + while( j < nBufLen - 2 && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'.') + || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + ) + ){ + j++; + } + if( j == 0 || j >= nBufLen - 2 ){ + return FALSE; + } + if( L'@' != pszBuf[j] ){ + return FALSE; + } +// nAtPos = j; + j++; + nDotCount = 0; +// nAlphaCount = 0; + + for (;;) { + nBgn = j; + while( j < nBufLen && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'-') + || (pszBuf[j] == L'_') + ) + ){ + j++; + } + if( 0 == j - nBgn ){ + return FALSE; + } + if( L'.' != pszBuf[j] ){ + if( 0 == nDotCount ){ + return FALSE; + }else{ + break; + } + }else{ + nDotCount++; + j++; + } + } + if( NULL != pnAddressLenfth ){ + *pnAddressLenfth = j; + } + return TRUE; +} diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp new file mode 100644 index 0000000000..65d63a68d6 --- /dev/null +++ b/tests/unittests/test-is_mailaddress.cpp @@ -0,0 +1,187 @@ +#include + +#define NOMINMAX +#include +#include + +// テスト対象関数のヘッダファイル +//#include "util/string_ex.h" //依存関係が多いのでテスト対象の関数定義のみ抜き出し +BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); + +// 変更前実装の定義 +// ※関数定義は IsMailAddress_20160427.cpp を参照。 +BOOL IsMailAddress_20160427(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); + +////////////////////////////////////////////////////////////////////// +// テストマクロ + +// 新旧動作比較用マクロ1(TRUE, FALSE向け) +// ASSERT_SAME: 旧実装と新実装で動作が変わらないことを期待 +#define ASSERT_SAME(expected, szTarget, cchTarget, pchMatchedLen) \ + EXPECT_##expected(_OLD_IMPL(szTarget, cchTarget, pchMatchedLen)); \ + ASSERT_##expected(_NEW_IMPL(szTarget, cchTarget, pchMatchedLen)); + +// 新旧動作比較用マクロ2(TRUE, FALSE向け) +// ASSERT_CHANGE: 旧実装と新実装で動作が変わることを期待 +#define ASSERT_CHANGE(expected, szTarget, cchTarget, pchMatchedLen) \ + EXPECT_NE(expected, _OLD_IMPL(szTarget, cchTarget, pchMatchedLen)); \ + ASSERT_##expected(_NEW_IMPL(szTarget, cchTarget, pchMatchedLen)); + +// テスト対象の新旧関数をマクロに当てる +#define _OLD_IMPL IsMailAddress_20160427 +#define _NEW_IMPL IsMailAddress + +////////////////////////////////////////////////////////////////////// +// テストコード + +TEST(testIsMailAddress, CheckBlank) +{ + wchar_t szTest[] = L""; //空文字 + ASSERT_SAME(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckExample) +{ + wchar_t szTest[] = L"test@example.com"; //標準的なサンプルメールアドレス + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckExampleCoJp) +{ + wchar_t szTest[] = L"test@example.co.jp"; //標準的なサンプルメールアドレス + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckTrailingSpace) +{ + wchar_t szTest[] = L"test@example.co.jp "; //標準的なサンプルメールアドレス + int mailboxLength; + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); + ASSERT_EQ(_countof(szTest) - 2, mailboxLength); +} + +TEST(testIsMailAddress, CheckPunctuation) +{ + wchar_t szTest[] = L"test!#$%&'*+-/=?^_`{|}~@example.com"; //記号類を含む + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckMaxLocalPart) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szTest, _countof(szTest), L"%s%s%s%s@example.com", szSeed, szSeed, szSeed, szSeed); //4個繋げて64文字にする + ASSERT_SAME(TRUE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckExceedMaxLocalPart) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szTest, _countof(szTest), L"%s%s%s%s0@example.com", szSeed, szSeed, szSeed, szSeed); //4個繋げて64文字 + 1 + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckMaxMailbox) +{ + wchar_t szTest[256]; + wchar_t szSeed64[64 + 1]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szSeed64, _countof(szSeed64), L"%s%s%s%s", szSeed, szSeed, szSeed, szSeed); //4個繋げて64文字にする + ::swprintf_s(szTest, _countof(szTest), L"%s@%.63s.%.63s.%.58s.com", szSeed64, szSeed64, szSeed64, szSeed64); //最大255文字のチェック + int mailboxLength; + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); + ASSERT_EQ(255, mailboxLength); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckMaxExceedMailbox) +{ + wchar_t szTest[256 + 1]; + wchar_t szSeed64[64 + 1]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szSeed64, _countof(szSeed64), L"%s%s%s%s", szSeed, szSeed, szSeed, szSeed); //4個繋げて64文字にする + ::swprintf_s(szTest, _countof(szTest), L"%s@%.63s.%.63s.%.58s0.com", szSeed64, szSeed64, szSeed64, szSeed64); //最大255文字オーバーのチェック + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckTooLongDomain) +{ + wchar_t szTest[256]; + wchar_t szSeed64[64 + 1]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szSeed64, _countof(szSeed64), L"%s%s%s%s", szSeed, szSeed, szSeed, szSeed); //4個繋げて64文字にする + ::swprintf_s(szTest, _countof(szTest), L"%s@%s.com", szSeed64, szSeed64); //63文字を超えるドメイン + ASSERT_CHANGE(FALSE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckTooShortDomain) +{ + wchar_t szTest[] = L"yajim@my.me"; //ドメイン部は3文字以上 + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckTooShortCCTLD) +{ + wchar_t szTest[] = L"test@test.c.bak"; //CCTLD部は2文字以上 + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckDomainIncludesUnderScore) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf(szTest, _countof(szTest), L"%s@test_domain.com", szSeed); //_を含むドメイン + ASSERT_CHANGE(FALSE, szTest, ::wcslen(szTest), NULL); +} + +TEST(testIsMailAddress, CheckDomainIncludesSingleHyphen) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szTest, _countof(szTest), L"%s@test-domain.com", szSeed); //途中に-を含むドメイン + ASSERT_SAME(TRUE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckDomainIncludesDoubleHyphen) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::swprintf_s(szTest, _countof(szTest), L"%s@test--domain.com", szSeed); //途中に-を含むドメイン + ASSERT_CHANGE(FALSE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckQuotedLocalPart) +{ + wchar_t szTest[] = L"\"test\\@c\"@test.com"; + ASSERT_CHANGE(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckBadQuotedLocalPart) +{ + wchar_t szTest[] = L"\"test@test.com"; + ASSERT_SAME(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// レビューコメントにより追試。動作変えていない部分だが、誤って動作が変わっていた。 +TEST(testIsMailAddress, CheckAwithAtmark) +{ + wchar_t szTest[] = L"a@"; + ASSERT_SAME(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +////////////////////////////////////////////////////////////////////// +// テストマクロの後始末 + +#undef _OLD_IMPL +#undef _NEW_IMPL + +#undef ASSERT_SAME +#undef ASSERT_CHANGE From 6a7ff2666fb5ac6f91c349bd4627f74cfc2f5984 Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Thu, 21 Mar 2019 10:50:40 +0900 Subject: [PATCH 2/7] =?UTF-8?q?tests/unittests/test-is=5Fmailaddress.cpp?= =?UTF-8?q?=20=E4=BB=A5=E5=A4=96=E3=81=AE=E9=83=A8=E5=88=86=E3=81=AB?= =?UTF-8?q?=E9=96=A2=E3=81=97=E3=81=A6=20a722f9c86f2860606b3f9e7dc79bb1692?= =?UTF-8?q?94cfaea=20=E3=82=92=20revert=20=E3=81=99=E3=82=8B=E5=89=8D?= =?UTF-8?q?=E3=81=AE=E7=8A=B6=E6=85=8B=E3=81=AB=E3=81=99=E3=82=8B=20tests/?= =?UTF-8?q?unittests/test-is=5Fmailaddress.cpp=20=E3=81=A7=20ASSERT=5FSAME?= =?UTF-8?q?=20=E3=81=A8=20ASSERT=5FCHANGE=20=E3=81=AE=E5=AE=9A=E7=BE=A9?= =?UTF-8?q?=E3=81=A7=20IsMailAddress=20=E3=81=AE=E3=81=BF=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B=20=5FNEW=5FIMPL=20=E3=81=8A=E3=82=88=E3=81=B3=20=5FOL?= =?UTF-8?q?D=5FIMPL=20=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/parse/CWordParse.cpp | 199 +++++++++ sakura_core/parse/CWordParse.h | 13 + sakura_core/util/string_ex.cpp | 415 ------------------ sakura_core/util/string_ex.h | 6 - sakura_core/view/CEditView.cpp | 1 - sakura_core/view/CEditView_Mouse.cpp | 1 - sakura_core/view/colors/CColor_Url.cpp | 3 +- .../unittests/code-IsMailAddress_20160427.cpp | 76 ---- tests/unittests/test-is_mailaddress.cpp | 17 +- 9 files changed, 215 insertions(+), 516 deletions(-) delete mode 100644 tests/unittests/code-IsMailAddress_20160427.cpp diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index a538fb3202..51684cad47 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -308,3 +308,202 @@ bool CWordParse::SearchNextWordPosition4KW( } return false; } + +//! wcがasciiなら0-127のまま返す。それ以外は0を返す。 +uchar_t wc_to_c(wchar_t wc) +{ +#if 0 +//! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 + char buf[3]={0,0,0}; + int ret=wctomb(buf,wc); + if(ret==-1)return 0; //エラー + if(buf[1]!=0)return 0; //エラー扱い + return buf[0] <= 0x7F ? buf[0]: 0; //1バイトで表せたので、これを返す 2011.12.17 バッファオーバーランの修正 +#endif + // 2011.12.15 wctombを使わない版 + if(wc <= 0x7F){ + return (uchar_t)wc; + } + return 0; +} + +//@@@ 2002.01.24 Start by MIK +/*! + 文字列がURLかどうかを検査する。 + + @retval TRUE URLである + @retval FALSE URLでない + + @note 関数内に定義したテーブルは必ず static const 宣言にすること(性能に影響します)。 + url_char の値は url_table の配列番号+1 になっています。 + 新しい URL を追加する場合は #define 値を修正してください。 + url_table は頭文字がアルファベット順になるように並べてください。 + + 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 +*/ +BOOL IsURL( + const wchar_t* pszLine, //!< [in] 文字列 + int offset, //!< [in] 検査を開始する位置。 + int nLineLen, //!< [in] 文字列の長さ + int* pnMatchLen //!< [out] URLの長さ。offset からの距離。 +) +{ + struct _url_table_t { + wchar_t name[12]; + int length; + bool is_mail; + }; + static const struct _url_table_t url_table[] = { + /* アルファベット順 */ + { L"file://", 7, false }, /* 1 */ + { L"ftp://", 6, false }, /* 2 */ + { L"gopher://", 9, false }, /* 3 */ + { L"http://", 7, false }, /* 4 */ + { L"https://", 8, false }, /* 5 */ + { L"mailto:", 7, true }, /* 6 */ + { L"news:", 5, false }, /* 7 */ + { L"nntp://", 7, false }, /* 8 */ + { L"prospero://", 11, false }, /* 9 */ + { L"telnet://", 9, false }, /* 10 */ + { L"tp://", 5, false }, /* 11 */ //2004.02.02 + { L"ttp://", 6, false }, /* 12 */ //2004.02.02 + { L"wais://", 7, false }, /* 13 */ + { L"{", 0, false } /* 14 */ /* '{' is 'z'+1 : terminate */ + }; + +/* テーブルの保守性を高めるための定義 */ + const char urF = 1; + const char urG = 3; + const char urH = 4; + const char urM = 6; + const char urN = 7; + const char urP = 9; + const char urT = 10; + const char urW = 13; //2004.02.02 + + static const char url_char[] = { + /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +00: */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +10: */ + 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, /* +20: " !"#$%&'()*+,-./" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, /* +30: "0123456789:;<=>?" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* +40: "@ABCDEFGHIJKLMNO" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, -1, /* +50: "PQRSTUVWXYZ[\]^_" */ + 0, -1, -1, -1, -1, -1,urF,urG,urH, -1, -1, -1, -1,urM,urN, -1, /* +60: "`abcdefghijklmno" */ + urP, -1, -1, -1,urT, -1, -1,urW, -1, -1, -1, 0, 0, 0, -1, 0, /* +70: "pqrstuvwxyz{|}~ " */ + /* あと128バイト犠牲にすればif文を2箇所削除できる */ + /* 0 : not url char + * -1 : url char + * other: url head char --> url_table array number + 1 + */ + }; + + const wchar_t * const begin = pszLine + offset; + const wchar_t * const end = pszLine + nLineLen; + const struct _url_table_t *urlp; + int i; + + if( wc_to_c(*begin)==0 ) return FALSE; /* 2バイト文字 */ + if( 0 < url_char[wc_to_c(*begin)] ){ /* URL開始文字 */ + for(urlp = &url_table[url_char[wc_to_c(*begin)]-1]; urlp->name[0] == wc_to_c(*begin); urlp++){ /* URLテーブルを探索 */ + if( (urlp->length <= end - begin) && (auto_memcmp(urlp->name, begin, urlp->length) == 0) ){ /* URLヘッダは一致した */ + if( urlp->is_mail ){ /* メール専用の解析へ */ + if( IsMailAddress(begin, urlp->length, end - begin - urlp->length, pnMatchLen) ){ + *pnMatchLen = *pnMatchLen + urlp->length; + return TRUE; + } + return FALSE; + } + for(i = urlp->length; i < end - begin; i++){ /* 通常の解析へ */ + if( wc_to_c(begin[i])==0 || (!(url_char[wc_to_c(begin[i])])) ) break; /* 終端に達した */ + } + if( i == urlp->length ) return FALSE; /* URLヘッダだけ */ + *pnMatchLen = i; + return TRUE; + } + } + } + return IsMailAddress(pszLine, offset, nLineLen, pnMatchLen); +} + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 +*/ +BOOL IsMailAddress( const wchar_t* pszBuf, int offset, int nBufLen, int* pnAddressLenfth ) +{ + struct { + bool operator()(const wchar_t ch) + { + return 0x21 <= ch && ch <= 0x7E && NULL == wcschr(L"\"(),:;<>@[\\]", ch); + } + } IsValidChar; + +/* + 直前の文字を利用した境界判定 +*/ + if (0 < offset && IsValidChar(pszBuf[offset-1])) { + return FALSE; + } + + pszBuf += offset; + nBufLen -= offset; + offset = 0; + + int j; + int nDotCount; + int nBgn; + + + j = 0; + if(pszBuf[j] != L'.' && IsValidChar(pszBuf[j])){ + j++; + }else{ + return FALSE; + } + while( j < nBufLen - 2 && IsValidChar(pszBuf[j]) ){ + j++; + } + if( j == 0 || j >= nBufLen - 2 ){ + return FALSE; + } + if( L'@' != pszBuf[j] ){ + return FALSE; + } +// nAtPos = j; + j++; + nDotCount = 0; +// nAlphaCount = 0; + + + for (;;) { + nBgn = j; + while( j < nBufLen && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'-') + || (pszBuf[j] == L'_') + ) + ){ + j++; + } + if( 0 == j - nBgn ){ + return FALSE; + } + if( L'.' != pszBuf[j] ){ + if( 0 == nDotCount ){ + return FALSE; + }else{ + break; + } + }else{ + nDotCount++; + j++; + } + } + if( NULL != pnAddressLenfth ){ + *pnAddressLenfth = j; + } + return TRUE; +} diff --git a/sakura_core/parse/CWordParse.h b/sakura_core/parse/CWordParse.h index 315e553025..ba59fd6521 100644 --- a/sakura_core/parse/CWordParse.h +++ b/sakura_core/parse/CWordParse.h @@ -125,6 +125,19 @@ class CWordParse{ static bool _match_charlist( const WCHAR c, const WCHAR *pszList ); }; +BOOL IsURL( const wchar_t* psz, int offset, int length, int* outLength);/* offset 引数の追加により境界判定が行える高速版 */ +inline +BOOL IsURL( const wchar_t* psz, int length, int* outLength) /* 指定アドレスがURLの先頭ならばTRUEとその長さを返す。高速版の追加により obsolete. */ +{ + return IsURL(psz, 0, length, outLength); +} +BOOL IsMailAddress( const wchar_t* psz, int offset, int length, int* outLength); /* offset 引数の追加により境界判定が行える高速版 */ +inline +BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength) /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ +{ + return IsMailAddress(psz, 0, length, outLength); +} + // ACHAR 版 inline bool CWordParse::_match_charlist( const ACHAR c, const ACHAR *pszList ) { diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index ba592117f3..da7b86b34e 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -871,418 +871,3 @@ int skr_towlower( int c ) #endif return towlower( (wchar_t)c ); } - -//! wcがasciiなら0-127のまま返す。それ以外は0を返す。 -inline static uchar_t wc_to_c(wchar_t wc) -{ -#if 0 -//! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 - char buf[3]={0,0,0}; - int ret=wctomb(buf,wc); - if(ret==-1)return 0; //エラー - if(buf[1]!=0)return 0; //エラー扱い - return buf[0] <= 0x7F ? buf[0]: 0; //1バイトで表せたので、これを返す 2011.12.17 バッファオーバーランの修正 -#endif - // 2011.12.15 wctombを使わない版 - if(wc <= 0x7F){ - return (uchar_t)wc; - } - return 0; -} - -/*! - 文字列がURLかどうかを検査する。 - - @retval TRUE URLである - @retval FALSE URLでない - - @note 関数内に定義したテーブルは必ず static const 宣言にすること(性能に影響します)。 - url_char の値は url_table の配列番号+1 になっています。 - 新しい URL を追加する場合は #define 値を修正してください。 - url_table は頭文字がアルファベット順になるように並べてください。 - - 2002.01.24 MIK - 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 -*/ -BOOL IsURL( - const wchar_t* pszLine, //!< [in] 文字列 - int nLineLen, //!< [in] 文字列の長さ - int* pnMatchLen //!< [out] URLの長さ -) -{ - // TODO: この関数は、UNCアドレスも含めて見直した方がよさげ by berryzplus - struct _url_table_t { - wchar_t name[12]; - int length; - bool is_mail; - }; - static const struct _url_table_t url_table[] = { - /* アルファベット順 */ - { L"file://", 7, false }, /* 1 */ - { L"ftp://", 6, false }, /* 2 */ - { L"gopher://", 9, false }, /* 3 */ - { L"http://", 7, false }, /* 4 */ - { L"https://", 8, false }, /* 5 */ - { L"mailto:", 7, true }, /* 6 */ - { L"news:", 5, false }, /* 7 */ - { L"nntp://", 7, false }, /* 8 */ - { L"prospero://", 11, false }, /* 9 */ - { L"telnet://", 9, false }, /* 10 */ - { L"tp://", 5, false }, /* 11 */ //2004.02.02 - { L"ttp://", 6, false }, /* 12 */ //2004.02.02 - { L"wais://", 7, false }, /* 13 */ - { L"{", 0, false } /* 14 */ /* '{' is 'z'+1 : terminate */ - }; - -/* テーブルの保守性を高めるための定義 */ - constexpr char urF = 1; - constexpr char urG = 3; - constexpr char urH = 4; - constexpr char urM = 6; - constexpr char urN = 7; - constexpr char urP = 9; - constexpr char urT = 10; - constexpr char urW = 13; //2004.02.02 - - static const char url_char[] = { - /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +00: */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +10: */ - 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, /* +20: " !"#$%&'()*+,-./" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, /* +30: "0123456789:;<=>?" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* +40: "@ABCDEFGHIJKLMNO" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, -1, /* +50: "PQRSTUVWXYZ[\]^_" */ - 0, -1, -1, -1, -1, -1,urF,urG,urH, -1, -1, -1, -1,urM,urN, -1, /* +60: "`abcdefghijklmno" */ - urP, -1, -1, -1,urT, -1, -1,urW, -1, -1, -1, 0, 0, 0, -1, 0, /* +70: "pqrstuvwxyz{|}~ " */ - /* あと128バイト犠牲にすればif文を2箇所削除できる */ - /* 0 : not url char - * -1 : url char - * other: url head char --> url_table array number + 1 - */ - }; - - const wchar_t *p = pszLine; - const struct _url_table_t *urlp; - int i; - - if( wc_to_c(*p)==0 ) return FALSE; /* 2バイト文字 */ - if( 0 < url_char[wc_to_c(*p)] ){ /* URL開始文字 */ - for(urlp = &url_table[url_char[wc_to_c(*p)]-1]; urlp->name[0] == wc_to_c(*p); urlp++){ /* URLテーブルを探索 */ - if( (urlp->length <= nLineLen) && (auto_memcmp(urlp->name, pszLine, urlp->length) == 0) ){ /* URLヘッダは一致した */ - p += urlp->length; /* URLヘッダ分をスキップする */ - if( urlp->is_mail ){ /* メール専用の解析へ */ - if( IsMailAddress(p, nLineLen - urlp->length, pnMatchLen) ){ - *pnMatchLen = *pnMatchLen + urlp->length; - return TRUE; - } - return FALSE; - } - for(i = urlp->length; i < nLineLen; i++, p++){ /* 通常の解析へ */ - if( wc_to_c(*p)==0 || (!(url_char[wc_to_c(*p)])) ) break; /* 終端に達した */ - } - if( i == urlp->length ) return FALSE; /* URLヘッダだけ */ - *pnMatchLen = i; - return TRUE; - } - } - } - return IsMailAddress(pszLine, nLineLen, pnMatchLen); // TODO: FALSE じゃなくて? - // この関数の作成目的は「指定した文字列がURLかどうかを判定すること」だったと推定される。 - // 先行する「mailto:」のないメールアドレスはURLではないのでここはFALSEを返すべき。 - // 「URLとメールアドレスにリンクを付けたい」を叶えるための措置と考えられるが、もっといい解決策がありそうに思う。 - // とはいえ、すぐに着手できそうな状況ではないので、備忘目的でコメントだけ残しておく。 by berryzplus -} - -// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept; - -// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する -inline static bool IsMailAddressDomain( - _In_z_ const wchar_t* pszAtmark, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszEndOfMailBox -) noexcept; - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す - @date 2016.04.27 記号類を許可 - @date 2018.09.09 RFC準拠 -*/ -BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) -{ - // RFC5321による mailbox の最大文字数 - constexpr ptrdiff_t MAX_MAILBOX = 255; //255オクテット - - // mailboxの最小文字数(これより短いと構成要素を含めなくなる) - // 例) a@z.jp - // 1(@手前) + 1(@) + 1(ドメイン) + 1(.) + 2(TLD/ccTLD) = 6 - constexpr ptrdiff_t MIN_MAILBOX = 6; - - // 想定しないパラメータは前半チェックの前に弾く - if (pszBuf == nullptr || nBufLen < MIN_MAILBOX) return FALSE; - - // メールアドレスには必ず@が含まれる - const wchar_t* pszAtmark; - - // メールアドレス前半部分(@の手前)をチェックする - if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { - return FALSE; - } - - // メールアドレスの終了位置を受け取るポインタを宣言する - const wchar_t* pszEndOfMailBox; - - // メールアドレス後半部分(@の後ろ)をチェックする - if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) - { - return FALSE; - } - - // 全体の長さが制限を超えていないかチェックする - const auto cchAddressLength = pszEndOfMailBox - pszBuf; - if (MAX_MAILBOX < cchAddressLength) - { - return FALSE; // 文字数オーバー - } - - if (pnAddressLength != nullptr) - { - *pnAddressLength = cchAddressLength; - } - return TRUE; -} - -/*! - * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する - * - * 高速化のため単純化した条件でチェックしている - * 参照する標準は RFC5321 - * @see http://srgia.com/docs/rfc5321j.html - */ -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept -{ - // RFC5321による local-part の最大文字数 - constexpr ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット - - // 関数仕様 - assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない - assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない - - // 出力値を初期化する - *ppszAtmark = nullptr; - - // 文字列が二重引用符で始まっているかチェックして結果を保存 - const bool quoted = (L'"' == *pszStart); - - // ループ中にスキャンする文字位置を設定する - auto pszScan = pszStart + (quoted ? 1 : 0); - auto pszScanEnd = std::min(pszStart + MAX_LOCAL_PART + 1, pszEnd); - - // スキャン位置が終端に達するまでループ - while (pszScan < pszScanEnd) - { - switch (*pszScan) - { - case L'@': - if (pszStart == pszScan) - { - return false; // local-partは1文字以上なのでNG - } - if (quoted) - { - return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG - } - *ppszAtmark = pszScan; - return true; // ここが正常終了 - case L'\\': // エスケープ記号 - if (pszScan + 1 == pszScanEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) - { - return false; - } - pszScan++; // エスケープ記号の分1文字進める - break; - case L'"': // 二重引用符 - if (quoted && pszScan + 1 < pszScanEnd && L'@' == pszScan[1]) - { - *ppszAtmark = &pszScan[1]; - return true; // ここは準正常終了。正常終了とはあえて区別しない。 - } - return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 - } - pszScan++; - } - return false; // 文字数オーバー -} - -/*! - * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する - */ -inline static bool IsMailAddressDomain( - _In_z_ const wchar_t* pszAtmark, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszEndOfMailBox -) noexcept -{ - // ccTLDの最小文字数 - constexpr ptrdiff_t MIN_TLD = 2; - - // ドメインの最小文字数 - constexpr ptrdiff_t MIN_DOMAIN = 3; - - // ドメインの最大文字数 - constexpr ptrdiff_t MAX_DOMAIN = 63; - - // 関数仕様 - assert(pszAtmark < pszEnd); // @位置と終了位置は逆転してはならない - assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない - - // 出力値を初期化する - *ppszEndOfMailBox = nullptr; - - // ループ中にスキャンする文字位置を設定する - auto pszScan = pszAtmark + 1; - if (pszScan == pszEnd) - { - return false; // @の後ろが0文字、長さが足りない - } - - auto dotCount = 0; - auto domainLength = 0; - auto prevHyphen = false; - - // スキャン位置が終端に達するまでループ - while (pszScan < pszEnd) - { - switch (*pszScan) - { - case L'.': // ドット記号 - if (dotCount == 0 && domainLength < MIN_DOMAIN) - { - return false; // ドメイン名の最小文字数は3なのでNG - } - if (0 < dotCount && domainLength < MIN_TLD) - { - // これはco.jpなどを正しく認識させるために必要。 - return false; // ドットで区切られる部分の最小文字数は2なのでNG - } - if (prevHyphen) - { - return false; // ハイフンに続くドットはNG - } - dotCount++; - domainLength = 0; - prevHyphen = false; - break; - case L'-': // ハイフン記号 - if (domainLength == 0) - { - return false; // ドットに続くハイフンはNG - } - if (prevHyphen) - { - return false; // 連続するハイフンはNG - } - domainLength++; - prevHyphen = true; - break; - default: - if (dotCount == 0) - { - return false; // ドメイン部には一つ以上のドット記号が必要なのでNG - } - if (domainLength == 0) - { - return false; // ドットで終わるドメインはNG - } - if (prevHyphen) - { - return false; // ハイフンで終わるドメインはNG - } - *ppszEndOfMailBox = pszScan; - return true; // ここも正常終了 - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - case L'A': - case L'B': - case L'C': - case L'D': - case L'E': - case L'F': - case L'G': - case L'H': - case L'I': - case L'J': - case L'K': - case L'L': - case L'M': - case L'N': - case L'O': - case L'P': - case L'Q': - case L'R': - case L'S': - case L'T': - case L'U': - case L'V': - case L'W': - case L'X': - case L'Y': - case L'Z': - case L'a': - case L'b': - case L'c': - case L'd': - case L'e': - case L'f': - case L'g': - case L'h': - case L'i': - case L'j': - case L'k': - case L'l': - case L'm': - case L'n': - case L'o': - case L'p': - case L'q': - case L'r': - case L's': - case L't': - case L'u': - case L'v': - case L'w': - case L'x': - case L'y': - case L'z': - domainLength++; - prevHyphen = false; - break; - } - pszScan++; - if (MAX_DOMAIN < domainLength) - { - return false; // 文字数オーバー - } - } - if (pszScan == pszEnd) - { - *ppszEndOfMailBox = pszScan; - return true; // ここが正常終了 - } - return false; -} diff --git a/sakura_core/util/string_ex.h b/sakura_core/util/string_ex.h index a0a19acbcb..cbb515da92 100644 --- a/sakura_core/util/string_ex.h +++ b/sakura_core/util/string_ex.h @@ -328,11 +328,5 @@ inline int wcsncmp_auto(const wchar_t* strData1, const wchar_t* szData2) #define _tcsncmp_literal strncmp_literal #endif -/* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ -BOOL IsURL(const wchar_t* pszLine, int nLineLen, int* pnMatchLen); - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す */ -BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLength); - #endif /* SAKURA_STRING_EX_29EB1DD7_7259_4D6C_A651_B9174E5C3D3C9_H_ */ /*[EOF]*/ diff --git a/sakura_core/view/CEditView.cpp b/sakura_core/view/CEditView.cpp index e9605686b2..a50b2b586e 100644 --- a/sakura_core/view/CEditView.cpp +++ b/sakura_core/view/CEditView.cpp @@ -55,7 +55,6 @@ #include "types/CTypeSupport.h" #include "convert/CConvert.h" #include "util/RegKey.h" -#include "util/string_ex.h" // IsURL #include "util/string_ex2.h" #include "util/os.h" //WM_MOUSEWHEEL,IMR_RECONVERTSTRING,WM_XBUTTON*,IMR_CONFIRMRECONVERTSTRING #include "util/module.h" diff --git a/sakura_core/view/CEditView_Mouse.cpp b/sakura_core/view/CEditView_Mouse.cpp index 9ce8818aae..196223eda9 100644 --- a/sakura_core/view/CEditView_Mouse.cpp +++ b/sakura_core/view/CEditView_Mouse.cpp @@ -36,7 +36,6 @@ #include "uiparts/CWaitCursor.h" #include "uiparts/HandCursor.h" #include "util/input.h" -#include "util/string_ex.h" // IsMailAddress #include "util/os.h" #include "sakura_rc.h" diff --git a/sakura_core/view/colors/CColor_Url.cpp b/sakura_core/view/colors/CColor_Url.cpp index 43f17daec0..03d6c04de2 100644 --- a/sakura_core/view/colors/CColor_Url.cpp +++ b/sakura_core/view/colors/CColor_Url.cpp @@ -6,7 +6,6 @@ #include "doc/CEditDoc.h" #include "doc/layout/CLayout.h" #include "types/CTypeSupport.h" -#include "util/string_ex.h" // IsURL // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // // URL // @@ -19,7 +18,7 @@ bool CColor_Url::BeginColor(const CStringRef& cStr, int nPos) int nUrlLen; if( _IsPosKeywordHead(cStr,nPos) /* URLを表示する */ - && IsURL( cStr.GetPtr() + nPos, cStr.GetLength() - nPos, &nUrlLen ) /* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ + && IsURL( cStr.GetPtr(), nPos, cStr.GetLength(), &nUrlLen ) /* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ ){ this->m_nCOMMENTEND = nPos + nUrlLen; return true; diff --git a/tests/unittests/code-IsMailAddress_20160427.cpp b/tests/unittests/code-IsMailAddress_20160427.cpp deleted file mode 100644 index 926c6936f1..0000000000 --- a/tests/unittests/code-IsMailAddress_20160427.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// テスト対象の旧関数(コピペで埋め込み) -#include "StdAfx.h" - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す - @date 2016.04.27 記号類を許可 -*/ -BOOL IsMailAddress_20160427( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) -{ - int j; - int nDotCount; - int nBgn; - - j = 0; - if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) - ){ - j++; - }else{ - return FALSE; - } - while( j < nBufLen - 2 && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'.') - || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) - ) - ){ - j++; - } - if( j == 0 || j >= nBufLen - 2 ){ - return FALSE; - } - if( L'@' != pszBuf[j] ){ - return FALSE; - } -// nAtPos = j; - j++; - nDotCount = 0; -// nAlphaCount = 0; - - for (;;) { - nBgn = j; - while( j < nBufLen && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'-') - || (pszBuf[j] == L'_') - ) - ){ - j++; - } - if( 0 == j - nBgn ){ - return FALSE; - } - if( L'.' != pszBuf[j] ){ - if( 0 == nDotCount ){ - return FALSE; - }else{ - break; - } - }else{ - nDotCount++; - j++; - } - } - if( NULL != pnAddressLenfth ){ - *pnAddressLenfth = j; - } - return TRUE; -} diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index 65d63a68d6..2eb7347073 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -8,28 +8,18 @@ //#include "util/string_ex.h" //依存関係が多いのでテスト対象の関数定義のみ抜き出し BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); -// 変更前実装の定義 -// ※関数定義は IsMailAddress_20160427.cpp を参照。 -BOOL IsMailAddress_20160427(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); - ////////////////////////////////////////////////////////////////////// // テストマクロ // 新旧動作比較用マクロ1(TRUE, FALSE向け) // ASSERT_SAME: 旧実装と新実装で動作が変わらないことを期待 #define ASSERT_SAME(expected, szTarget, cchTarget, pchMatchedLen) \ - EXPECT_##expected(_OLD_IMPL(szTarget, cchTarget, pchMatchedLen)); \ - ASSERT_##expected(_NEW_IMPL(szTarget, cchTarget, pchMatchedLen)); + EXPECT_##expected(IsMailAddress(szTarget, cchTarget, pchMatchedLen)) // 新旧動作比較用マクロ2(TRUE, FALSE向け) // ASSERT_CHANGE: 旧実装と新実装で動作が変わることを期待 #define ASSERT_CHANGE(expected, szTarget, cchTarget, pchMatchedLen) \ - EXPECT_NE(expected, _OLD_IMPL(szTarget, cchTarget, pchMatchedLen)); \ - ASSERT_##expected(_NEW_IMPL(szTarget, cchTarget, pchMatchedLen)); - -// テスト対象の新旧関数をマクロに当てる -#define _OLD_IMPL IsMailAddress_20160427 -#define _NEW_IMPL IsMailAddress + EXPECT_NE(expected, IsMailAddress(szTarget, cchTarget, pchMatchedLen)) ////////////////////////////////////////////////////////////////////// // テストコード @@ -180,8 +170,5 @@ TEST(testIsMailAddress, CheckAwithAtmark) ////////////////////////////////////////////////////////////////////// // テストマクロの後始末 -#undef _OLD_IMPL -#undef _NEW_IMPL - #undef ASSERT_SAME #undef ASSERT_CHANGE From f30b78a195906b15e2cf7b2c5e59471d46b7ed74 Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Thu, 21 Mar 2019 10:56:23 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unittests/test-is_mailaddress.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index 2eb7347073..ff1cfdcae6 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -11,6 +11,13 @@ BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); ////////////////////////////////////////////////////////////////////// // テストマクロ +// 新動作 = PR #421 導入によって変更されるはずだった IsMailAddress() +// 旧動作 = PR #421 導入前、revert 後の IsMailAddress() +// +// PR #421 導入によって IsMailAddress() の単体テストが実装されたが、 +// PR #421 の revert によって消された単体テストを復活する。 +// 将来 IsMailAddress() の仕様を変更する場合はこの単体テストを必要に応じて修正すればよい。 + // 新旧動作比較用マクロ1(TRUE, FALSE向け) // ASSERT_SAME: 旧実装と新実装で動作が変わらないことを期待 #define ASSERT_SAME(expected, szTarget, cchTarget, pchMatchedLen) \ From b91732ff02d5202e5f5dd4b67c180cecef533664 Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Thu, 21 Mar 2019 10:57:13 +0900 Subject: [PATCH 4/7] =?UTF-8?q?copyright=20=E3=82=B3=E3=83=A1=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unittests/test-is_mailaddress.cpp | 26 ++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index ff1cfdcae6..69ded3711a 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -1,4 +1,28 @@ -#include +/*! @file */ +/* + Copyright (C) 2018-2019 Sakura Editor Organization + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#include #define NOMINMAX #include From 2527b33ecbecd718101cc7e6c549dfd3e947d0ec Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Thu, 21 Mar 2019 13:10:59 +0900 Subject: [PATCH 5/7] =?UTF-8?q?IsMailAddress=20=E3=81=AE=E5=BC=95=E6=95=B0?= =?UTF-8?q?=203=20=E3=81=A4=E7=89=88=E3=82=92=20inline=20=E9=96=A2?= =?UTF-8?q?=E6=95=B0=E3=81=8B=E3=82=89=E9=80=9A=E5=B8=B8=E3=81=AE=E9=96=A2?= =?UTF-8?q?=E6=95=B0=E3=81=AB=E5=A4=89=E6=9B=B4=20(MinGW=20=E3=81=A7?= =?UTF-8?q?=E3=81=AE=E3=82=B3=E3=83=B3=E3=83=91=E3=82=A4=E3=83=AB=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/parse/CWordParse.cpp | 5 +++++ sakura_core/parse/CWordParse.h | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 51684cad47..abcd7aa5f9 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -507,3 +507,8 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int offset, int nBufLen, int* pnAddre } return TRUE; } + +BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength) /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ +{ + return IsMailAddress(psz, 0, length, outLength); +} diff --git a/sakura_core/parse/CWordParse.h b/sakura_core/parse/CWordParse.h index ba59fd6521..3169d55cab 100644 --- a/sakura_core/parse/CWordParse.h +++ b/sakura_core/parse/CWordParse.h @@ -132,11 +132,7 @@ BOOL IsURL( const wchar_t* psz, int length, int* outLength) /* 指定アドレ return IsURL(psz, 0, length, outLength); } BOOL IsMailAddress( const wchar_t* psz, int offset, int length, int* outLength); /* offset 引数の追加により境界判定が行える高速版 */ -inline -BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength) /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ -{ - return IsMailAddress(psz, 0, length, outLength); -} +BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength); /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ // ACHAR 版 inline bool CWordParse::_match_charlist( const ACHAR c, const ACHAR *pszList ) From 8f13c533e1399f459a2006e84926559d063c4142 Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Sun, 24 Mar 2019 07:34:51 +0900 Subject: [PATCH 6/7] =?UTF-8?q?Revert=20"IsMailAddress=20=E3=81=AE?= =?UTF-8?q?=E5=BC=95=E6=95=B0=203=20=E3=81=A4=E7=89=88=E3=82=92=20inline?= =?UTF-8?q?=20=E9=96=A2=E6=95=B0=E3=81=8B=E3=82=89=E9=80=9A=E5=B8=B8?= =?UTF-8?q?=E3=81=AE=E9=96=A2=E6=95=B0=E3=81=AB=E5=A4=89=E6=9B=B4=20(MinGW?= =?UTF-8?q?=20=E3=81=A7=E3=81=AE=E3=82=B3=E3=83=B3=E3=83=91=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E5=AF=BE=E5=BF=9C)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2527b33ecbecd718101cc7e6c549dfd3e947d0ec. --- sakura_core/parse/CWordParse.cpp | 5 ----- sakura_core/parse/CWordParse.h | 6 +++++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index abcd7aa5f9..51684cad47 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -507,8 +507,3 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int offset, int nBufLen, int* pnAddre } return TRUE; } - -BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength) /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ -{ - return IsMailAddress(psz, 0, length, outLength); -} diff --git a/sakura_core/parse/CWordParse.h b/sakura_core/parse/CWordParse.h index 3169d55cab..ba59fd6521 100644 --- a/sakura_core/parse/CWordParse.h +++ b/sakura_core/parse/CWordParse.h @@ -132,7 +132,11 @@ BOOL IsURL( const wchar_t* psz, int length, int* outLength) /* 指定アドレ return IsURL(psz, 0, length, outLength); } BOOL IsMailAddress( const wchar_t* psz, int offset, int length, int* outLength); /* offset 引数の追加により境界判定が行える高速版 */ -BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength); /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ +inline +BOOL IsMailAddress( const wchar_t* psz, int length, int* outLength) /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す。高速版の追加により obsolete. */ +{ + return IsMailAddress(psz, 0, length, outLength); +} // ACHAR 版 inline bool CWordParse::_match_charlist( const ACHAR c, const ACHAR *pszList ) From cd6be8eb97d835eb065240cd162a58d5acf558ac Mon Sep 17 00:00:00 2001 From: Masaru Tsuchiyama Date: Sun, 24 Mar 2019 07:38:21 +0900 Subject: [PATCH 7/7] =?UTF-8?q?IsMailAddress=20=E3=82=92=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E5=8C=96=E3=81=97=E3=81=9F=E3=81=AE?= =?UTF-8?q?=E3=81=A7=20CWordParse.h=20=E3=82=92=E3=82=A4=E3=83=B3=E3=82=AF?= =?UTF-8?q?=E3=83=AB=E3=83=BC=E3=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unittests/test-is_mailaddress.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index 69ded3711a..5f0b3001b9 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -27,6 +27,7 @@ #define NOMINMAX #include #include +#include "parse/CWordParse.h" // テスト対象関数のヘッダファイル //#include "util/string_ex.h" //依存関係が多いのでテスト対象の関数定義のみ抜き出し