面倒なので、実際に使っているソースを載せます。解説はそのうち書きます。
このソースで、http, telnet, ftp はもちろん、 info, man, wais, ldap, ... いろんなスキームに対応です。足りなきゃ (chromeとかを追加したければ) scheme_types[] に追加して、scheme_types_count の値をインクリメントしておくように。
ちなみに、こっちのページで説明している isuric() 関数が必要です。
static const char* scheme_types[] = {
"http", "https", "ftp", "mailto", "news", "shttp", "telnet",
"file", "gopher", "man", "info", "wais", "ldap", "ntds", "whatis" // and more...
};
// obsoleted code.
// #define scheme_types_count 15
bool scheme_check(const char* src, const char* cmp, int len)
{
for (register int i=0; i<len; i++)
if (src[i] != cmp[i])
return false;
return true;
}
unsigned int find_scheme(const std::string& source, unsigned int begin_pos)
{
const char* src = source.c_str();
unsigned int slen = source.length();
for (unsigned int xpos = begin_pos; xpos < slen; xpos++) {
if (src[xpos] == ':'
&& isuric(src[xpos + 1])
) {
for (unsigned int i=3; i<=6; i++) {
if (xpos < i)
continue;
for (int j=0; j<sizeof(scheme_types)/sizeof(static const char *); j++) {
if (scheme_check(&src[xpos-i], scheme_types[j], i))
return xpos - i;
}
}
}
}
return -1;
}
std::string html_anchor(const std::string& source)
{
std::string result = source;
unsigned int begin_pos = 0;
while ((begin_pos = find_scheme(result, begin_pos)) != -1) {
unsigned int end_pos = begin_pos;
while (end_pos < result.length() && isuric(result[end_pos]))
end_pos++;
std::string url = result.substr(begin_pos, end_pos - begin_pos);
result.erase(begin_pos, url.length());
url = "<a href=\"" + url + "\" target=\"_top\">" + url + "</a>";
result.insert(begin_pos, url);
begin_pos += url.length();
}
return result;
}
簡単に説明すると、すべての URI に共通な文字 ':' を探し、その前がスキームのリストにあり、かつその直後の文字が uri 文字 であるか、を確認し、isuric() が成立する範囲を URI として認識し、そこにアンカー ( <a href=...> タグ) を追加している。