<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cai Guanhao</title>
	<atom:link href="http://www.caiguanhao.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.caiguanhao.com</link>
	<description>Just another WordPress site</description>
	<lastBuildDate>Sun, 06 May 2012 11:04:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>GitPHP：URL重写（伪静态化）</title>
		<link>http://www.caiguanhao.com/archives/207</link>
		<comments>http://www.caiguanhao.com/archives/207#comments</comments>
		<pubDate>Sun, 06 May 2012 08:43:29 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[Git]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=207</guid>
		<description><![CDATA[GitPHP默认是没有用URL重写的，所以里面的链接都是很原始的链接，又长又臭。如： http://localhost/index.php?p=jquery.git&#038;a=commit&#038;h=247d824d35e81ec96e1a8913674c97fb45dd40ae 为了解决这个问题，通常会用到 Apache 的 Rewrite 功能。首先修改GitPHP的代码。GitPHP是通过模板控制网页代码的，这个很方便，你基本上只需要在它的templates文件夹内工作就行。 当然你需要下载GitPHP代码：git clone http://git.gitphp.org/gitphp.git。 首先你要有一个能全文替换文字的工具，在Linux下通常会想到有grep命令，但是为避免自己出错，还是用一个比较好的图形化软件，regexxer。 首先我们要将 index\.php 替换为 / 。这个不多，只有几处。 接着是将 {\$SCRIPT_NAME}\?p= 替换为 /repos/ 。repos是我自己设置的，你可以自定义，如 p ，不过不要和存在的文件夹或文件名相同。 因为大多数链接的参数都是以 ?p= 、 ?p= &#038;a= 或 ?p= &#038;a= &#038;h= 这个形式和顺序存在，如果再继续就不保证合理，所以到这里就算了，后面继续有的参数，如 hb 就用问号附加参数。 将 &#38;amp;a= 替换为 / 。然后将 &#38;amp;h= 替换为 / 。 接着将 (href="[^;]+)&#38;amp; 替换为 $1? ，这样就可以将结尾的 &#038; 改为 ? 。 现在基本上可以了。不过还剩下搜索表单和切换语言表单、ATOM/RSS等页面的处理。 打开 [...]]]></description>
			<content:encoded><![CDATA[<p>GitPHP默认是没有用URL重写的，所以里面的链接都是很原始的链接，又长又臭。如：</p>
<p>http://localhost/index.php?p=jquery.git&#038;a=commit&#038;h=247d824d35e81ec96e1a8913674c97fb45dd40ae</p>
<p><img class="alignnone size-large wp-image-208" title="gitphp" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/05/gitphp_normal-576x436.png" alt="" width="576" height="436" /></p>
<p>为了解决这个问题，通常会用到 Apache 的 Rewrite 功能。首先修改GitPHP的代码。GitPHP是通过模板控制网页代码的，这个很方便，你基本上只需要在它的templates文件夹内工作就行。</p>
<p>当然你需要下载GitPHP代码：<code>git clone http://git.gitphp.org/gitphp.git</code>。</p>
<p>首先你要有一个能全文替换文字的工具，在Linux下通常会想到有grep命令，但是为避免自己出错，还是用一个比较好的图形化软件，regexxer。</p>
<p>首先我们要将 <code>index\.php</code> 替换为 / 。这个不多，只有几处。</p>
<p>接着是将 <code>{\$SCRIPT_NAME}\?p=</code> 替换为 /repos/ 。repos是我自己设置的，你可以自定义，如 p ，不过不要和存在的文件夹或文件名相同。</p>
<p>因为大多数链接的参数都是以 ?p= 、 ?p= &#038;a= 或 ?p= &#038;a= &#038;h= 这个形式和顺序存在，如果再继续就不保证合理，所以到这里就算了，后面继续有的参数，如 hb 就用问号附加参数。</p>
<p>将 <code>&amp;amp;a=</code> 替换为 / 。然后将 <code>&amp;amp;h=</code> 替换为 / 。</p>
<p>接着将 <code>(href="[^;]+)&amp;amp;</code> 替换为 $1? ，这样就可以将结尾的 &#038; 改为 ? 。</p>
<p>现在基本上可以了。不过还剩下搜索表单和切换语言表单、ATOM/RSS等页面的处理。</p>
<p>打开 main.tpl 找到 {$SCRIPT_NAME} 所在的 form 表单。</p>
<pre class="brush: xml; title: ; notranslate">
&lt;form action=&quot;{$SCRIPT_NAME}&quot; method=&quot;get&quot; id=&quot;frmLangSelect&quot;&gt;
	 &lt;div&gt;
{foreach from=$requestvars key=var item=val}
{if $var != &quot;l&quot;}
&lt;input type=&quot;hidden&quot; name=&quot;{$var}&quot; value=&quot;{$val|escape}&quot; /&gt;
{/if}
{/foreach}
&lt;label for=&quot;selLang&quot;&gt;{t}language:{/t}&lt;/label&gt;
&lt;select name=&quot;l&quot; id=&quot;selLang&quot;&gt;
  {foreach from=$supportedlocales key=locale item=language}
	&lt;option {if $locale == $currentlocale}selected=&quot;selected&quot;{/if} value=&quot;{$locale}&quot;&gt;{if $language}{$language} ({$locale}){else}{$locale}{/if}&lt;/option&gt;
  {/foreach}
&lt;/select&gt;
&lt;input type=&quot;submit&quot; value=&quot;{t}set{/t}&quot; id=&quot;btnLangSet&quot; /&gt;
	 &lt;/div&gt;
&lt;/form&gt;

更改为：

&lt;form action=&quot;/{if $requestvars}repos{foreach from=$requestvars key=var item=val}{if $var != &quot;l&quot;}/{$val|escape}{/if}{/foreach}{/if}&quot; method=&quot;get&quot; id=&quot;frmLangSelect&quot;&gt;
	 &lt;div&gt;
&lt;label for=&quot;selLang&quot;&gt;{t}language:{/t}&lt;/label&gt;
&lt;select name=&quot;l&quot; id=&quot;selLang&quot;&gt;
  {foreach from=$supportedlocales key=locale item=language}
	&lt;option {if $locale == $currentlocale}selected=&quot;selected&quot;{/if} value=&quot;{$locale}&quot;&gt;{if $language}{$language} ({$locale}){else}{$locale}{/if}&lt;/option&gt;
  {/foreach}
&lt;/select&gt;
&lt;input type=&quot;submit&quot; value=&quot;{t}set{/t}&quot; id=&quot;btnLangSet&quot; /&gt;
	 &lt;/div&gt;
&lt;/form&gt;
</pre>
<p>总而言之就是将 hidden 标记的内容转换为 form 的 action 参数。</p>
<p>同样道理，代码查找的也要更改，打开 projectbase.tpl ：</p>
<pre class="brush: xml; title: ; notranslate">
&lt;form method=&quot;get&quot; action=&quot;/&quot; enctype=&quot;application/x-www-form-urlencoded&quot;&gt;
  &lt;div class=&quot;search&quot;&gt;
	&lt;input type=&quot;hidden&quot; name=&quot;p&quot; value=&quot;{$project-&gt;GetProject()}&quot; /&gt;
	&lt;input type=&quot;hidden&quot; name=&quot;a&quot; value=&quot;search&quot; /&gt;
	&lt;input type =&quot;hidden&quot; name=&quot;h&quot; value=&quot;{if $commit}{$commit-&gt;GetHash()}{else}HEAD{/if}&quot; /&gt;
	&lt;select name=&quot;st&quot;&gt;
	  &lt;option {if $searchtype == 'commit'}selected=&quot;selected&quot;{/if} value=&quot;commit&quot;&gt;{t}commit{/t}&lt;/option&gt;
	  &lt;option {if $searchtype == 'author'}selected=&quot;selected&quot;{/if} value=&quot;author&quot;&gt;{t}author{/t}&lt;/option&gt;
	  &lt;option {if $searchtype == 'committer'}selected=&quot;selected&quot;{/if} value=&quot;committer&quot;&gt;{t}committer{/t}&lt;/option&gt;
	  {if $filesearch}
		&lt;option {if $searchtype == 'file'}selected=&quot;selected&quot;{/if} value=&quot;file&quot;&gt;{t}file{/t}&lt;/option&gt;
	  {/if}
	&lt;/select&gt; {t}search{/t}: &lt;input type=&quot;text&quot; name=&quot;s&quot; {if $search}value=&quot;{$search}&quot;{/if} /&gt;
  &lt;/div&gt;
&lt;/form&gt;

更改为：

&lt;form method=&quot;get&quot; action=&quot;/repos/{$project-&gt;GetProject()}/search/{if $commit}{$commit-&gt;GetHash()}{else}HEAD{/if}&quot; enctype=&quot;application/x-www-form-urlencoded&quot;&gt;
  &lt;div class=&quot;search&quot;&gt;
	&lt;select name=&quot;st&quot;&gt;
	  &lt;option {if $searchtype == 'commit'}selected=&quot;selected&quot;{/if} value=&quot;commit&quot;&gt;{t}commit{/t}&lt;/option&gt;
	  &lt;option {if $searchtype == 'author'}selected=&quot;selected&quot;{/if} value=&quot;author&quot;&gt;{t}author{/t}&lt;/option&gt;
	  &lt;option {if $searchtype == 'committer'}selected=&quot;selected&quot;{/if} value=&quot;committer&quot;&gt;{t}committer{/t}&lt;/option&gt;
	  {if $filesearch}
		&lt;option {if $searchtype == 'file'}selected=&quot;selected&quot;{/if} value=&quot;file&quot;&gt;{t}file{/t}&lt;/option&gt;
	  {/if}
	&lt;/select&gt; {t}search{/t}: &lt;input type=&quot;text&quot; name=&quot;s&quot; {if $search}value=&quot;{$search}&quot;{/if} /&gt;
  &lt;/div&gt;
&lt;/form&gt;
</pre>
<p>最后，清理一下全部页面，将剩余的 <code>{\$SCRIPT_NAME}</code> 替换为 / 。</p>
<p>对ATOM/RSS等页面，将 <code>{scripturl}?p=</code> 替换为 {scripturl}/repos/ 。repos和之前的一样。</p>
<p>因为 {scripturl} 包含了 index.php 所以要修改一下 include/smartyplugins/function.scripturl.php 。</p>
<pre class="brush: php; title: ; notranslate">
// 将：
$scriptstr .= $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
// 改为：
$scriptstr .= $_SERVER['HTTP_HOST'];
</pre>
<p>另外，最好在测试新的模板时，把template_c里面的全部缓存文件删除。</p>
<p>当然最重要的还是要设置 .htaccess 文件，QSA是附加QueryString的意思，L就是执行完这行就停了：</p>
<pre class="brush: plain; title: ; notranslate">
RewriteEngine on

RewriteRule ^repos/([^/&amp;]+)/([^/&amp;]+)/([^/&amp;]+)(.*)$ index.php?p=$1&amp;a=$2&amp;h=$3$4 [L,QSA]
RewriteRule ^repos/([^/&amp;]+)/([^/&amp;]+)(.*)$ index.php?p=$1&amp;a=$2$3 [L,QSA]
RewriteRule ^repos/([^/&amp;]+)(.*)$ index.php?p=$1$2 [L,QSA]
</pre>
<p>当然，如果要完全避免 index.php 可以将 index.php 改为一个其他人很难猜中的名字，如 023dbce5e060641d09218027704ca4b3.php ，然后随之更改 .htaccess 文件为：</p>
<pre class="brush: plain; title: ; notranslate">
RewriteEngine on

RewriteRule ^repos/([^/&amp;]+)/([^/&amp;]+)/([^/&amp;]+)(.*)$ 023dbce5e060641d09218027704ca4b3.php?p=$1&amp;a=$2&amp;h=$3$4 [L,QSA]
RewriteRule ^repos/([^/&amp;]+)/([^/&amp;]+)(.*)$ 023dbce5e060641d09218027704ca4b3.php?p=$1&amp;a=$2$3 [L,QSA]
RewriteRule ^repos/([^/&amp;]+)(.*)$ 023dbce5e060641d09218027704ca4b3.php?p=$1$2 [L,QSA]

RewriteRule ^$ 023dbce5e060641d09218027704ca4b3.php
</pre>
<p>最终，URL重新成功！变成了好看的URL了：</p>
<p>http://localhost/repos/jquery.git/commit/247d824d35e81ec96e1a8913674c97fb45dd40ae</p>
<p><img src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/05/gitphp_url_rewrite-576x436.png" alt="" title="GitPHP URL 重写" width="576" height="436" class="alignnone size-large wp-image-212" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/207/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GLib：Regex匹配所有</title>
		<link>http://www.caiguanhao.com/archives/205</link>
		<comments>http://www.caiguanhao.com/archives/205#comments</comments>
		<pubDate>Thu, 03 May 2012 11:19:24 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[GLib]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=205</guid>
		<description><![CDATA[利用OpenSSL获取证书信息，如X509_get_subject_name，有的证书会得到多个OU，假设信息如下：/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.bluehost.com。 如果正则表达式是 /OU=(.+?)/(OU&#124;CN)= ，用 g_regex_match 、 while (g_match_info_matches(match_info)) 和 g_match_info_next (match_info, NULL); 的方法只能获取第一个OU的值，如果用 g_regex_match_all 就会得到很多重复的值，而且步骤多了程序会变慢。 这个时候最好外加 g_regex_match_full ，找到匹配项之后再根据位置再匹配。]]></description>
			<content:encoded><![CDATA[<p>利用OpenSSL获取证书信息，如X509_get_subject_name，有的证书会得到多个OU，假设信息如下：<code>/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.bluehost.com</code>。</p>
<p>如果正则表达式是 <code>/OU=(.+?)/(OU|CN)=</code> ，用 <code>g_regex_match</code> 、 <code>while (g_match_info_matches(match_info))</code> 和 <code>g_match_info_next (match_info, NULL);</code> 的方法只能获取第一个OU的值，如果用 <code>g_regex_match_all</code> 就会得到很多重复的值，而且步骤多了程序会变慢。</p>
<p>这个时候最好外加 <code>g_regex_match_full</code> ，找到匹配项之后再根据位置再匹配。</p>
<pre class="brush: cpp; title: ; notranslate">
int pos;
...
pos = 0;
while (g_regex_match_full(regex, data, -1, pos, 0, &amp;match_info, NULL)) {
	while (g_match_info_matches(match_info)) {
		if (g_utf8_strlen(val, -1)&gt;0) val = g_strconcat(val, &quot;\n&quot;, NULL);
		val = g_strconcat(val, g_match_info_fetch(match_info, 1), NULL);
		g_match_info_fetch_pos(match_info, 1, NULL, &amp;pos); //更新pos
		g_match_info_next(match_info, NULL);
	}
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/205/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenSSH：SFTP初始目录</title>
		<link>http://www.caiguanhao.com/archives/199</link>
		<comments>http://www.caiguanhao.com/archives/199#comments</comments>
		<pubDate>Sun, 29 Apr 2012 05:36:21 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[SSH]]></category>
		<category><![CDATA[SFTP]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=199</guid>
		<description><![CDATA[用OpenSSH来创建SFTP，如果要指定验证后显示的初始目录，就编辑 /etc/ssh/sshd_config ，然后新增ChrootDirectory。 如果是用户组就用 Match Group 加用户组名。因为root权限问题，所以ssh重启后即使通过验证也不能登录。在curl里面显示的是 Failure initializing sftp session: Unable to startup channel 。 所以要到终端修改权限，用 sudo chown root.root /home/your_username 。 但要注意的是 chown 目录可能令系统登录拖慢，甚至出错因为权限错误。 参考：http://www.debian-administration.org/articles/590。]]></description>
			<content:encoded><![CDATA[<p>用OpenSSH来创建SFTP，如果要指定验证后显示的初始目录，就编辑 /etc/ssh/sshd_config ，然后新增ChrootDirectory。</p>
<pre class="brush: plain; title: ; notranslate">
Match User caiguanhao
	ChrootDirectory /home/%u
</pre>
<p>如果是用户组就用 Match Group 加用户组名。因为root权限问题，所以ssh重启后即使通过验证也不能登录。在curl里面显示的是 Failure initializing sftp session: Unable to startup channel 。</p>
<p><img class="alignnone size-full wp-image-200" title="sftp-log" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/sftp-log.png" alt="" width="475" height="166" /></p>
<p>所以要到终端修改权限，用 <code>sudo chown root.root /home/your_username</code> 。</p>
<p>但要注意的是 chown 目录可能令系统登录拖慢，甚至出错因为权限错误。</p>
<p>参考：<a href="http://www.debian-administration.org/articles/590">http://www.debian-administration.org/articles/590</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/199/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GTK+：更改widget属性而又不触发相应信号（如changed）</title>
		<link>http://www.caiguanhao.com/archives/196</link>
		<comments>http://www.caiguanhao.com/archives/196#comments</comments>
		<pubDate>Sun, 22 Apr 2012 18:59:19 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[GTK+]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=196</guid>
		<description><![CDATA[有时需要更改一些widget的属性，却又不想触发与之相关的信号，因为这样会多做一步造成错误。如 gtk_combo_box_set_active 必定会触发combo box的changed信号， gtk_toggle_button_set_active 必定会触发check button的toggled信号，等等。 当然不像网页JavaScript中的列表的change动作那样，只当用户手把手改变才会触发，用代码改变列表不会自动触发，除非改变之后才trigger(&#8216;change&#8217;)。 比较简便的方法是用 g_signal_handler_block 和 g_signal_handler_unblock 进行“包围”，它们之间的代码中如何更改值也不会触发相应的信号。 首先定义 gulong handler_id; 作为 g_signal_connect 的返回值，然后用这个 gulong 就可以控制信号的开关了。 示例：]]></description>
			<content:encoded><![CDATA[<p>有时需要更改一些widget的属性，却又不想触发与之相关的信号，因为这样会多做一步造成错误。如 <code>gtk_combo_box_set_active</code> 必定会触发combo box的changed信号， <code>gtk_toggle_button_set_active</code> 必定会触发check button的toggled信号，等等。</p>
<p>当然不像网页JavaScript中的列表的change动作那样，只当用户手把手改变才会触发，用代码改变列表不会自动触发，除非改变之后才trigger(&#8216;change&#8217;)。</p>
<p>比较简便的方法是用 <code>g_signal_handler_block</code> 和 <code>g_signal_handler_unblock</code> 进行“包围”，它们之间的代码中如何更改值也不会触发相应的信号。</p>
<p>首先定义 <code>gulong handler_id;</code> 作为 <code>g_signal_connect</code> 的返回值，然后用这个 gulong 就可以控制信号的开关了。</p>
<p>示例：</p>
<pre class="brush: cpp; title: ; notranslate">
...
gulong handler_id;
...
handler_id = g_signal_connect(toggled_button, &quot;toggled&quot;, G_CALLBACK(on_toggled), NULL);
...
g_signal_handler_block(toggled_button, handler_id);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggled_button), FALSE);
g_signal_handler_unblock(toggled_button, handler_id);
...
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/196/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>git mv：GitHub/Bitbucket/Assembla/Unfuddle文件移动对比</title>
		<link>http://www.caiguanhao.com/archives/187</link>
		<comments>http://www.caiguanhao.com/archives/187#comments</comments>
		<pubDate>Tue, 10 Apr 2012 09:11:10 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[Git]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=187</guid>
		<description><![CDATA[使用 git mv 命令可以移动文件，然后 push 到多个git存取点，对这些站点进行一个对比： GitHub中，浏览文件的历史有显示移动前的文件名和链接，可以快速浏览移动前的版本。 Bitbucket中，浏览文件的历史没有提示这个文件曾经移动过，但还可以按网址进入浏览移动前的版本的页面，如将 http://bb.caiguanhao.com/gftp/history/src/gFTP.c 改成 http://bb.caiguanhao.com/gftp/history/gFTP.c 即可。 Assembla中，浏览文件历史（Revision log）也没有提示，按Bitbucket的方法修改网址不行，网页这自动跳转到项目主页。只能通过changesets（即commits）里面查看。 Unfuddle中，文件历史也没要提示移动过，但可以用修改网址的方法。如点击进入 History 后网址是 http://caiguanhao.unfuddle.com/a#/repositories/1/history?path=/src/gFTP.c&#038;commit=d2619b5080ffd862492aa275254872803fe40c88，看到这行的parent是dd9ebc604ebd074c94a0d2c0a72c673fc87a1908（即移动前的commit），复制，然后分别更改网址的path和commit，成为 http://caiguanhao.unfuddle.com/a#/repositories/1/history?path=/gFTP.c&#038;commit=dd9ebc604ebd074c94a0d2c0a72c673fc87a1908 即可。 综合几个热门的免费存放点，GitHub比较人性化一点，当然最好一开始想好，first commit之后就不要移动了。]]></description>
			<content:encoded><![CDATA[<p>使用 <a href="http://linux.die.net/man/1/git-mv">git mv</a> 命令可以移动文件，然后 push 到多个git存取点，对这些站点进行一个对比：</p>
<p>GitHub中，浏览文件的历史有显示移动前的文件名和链接，可以快速浏览移动前的版本。</p>
<p><a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/github.png"><img class="alignnone size-full wp-image-188" title="github" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/github.png" alt="" width="484" height="297" /></a></p>
<p>Bitbucket中，浏览文件的历史没有提示这个文件曾经移动过，但还可以按网址进入浏览移动前的版本的页面，如将 <a href="http://bb.caiguanhao.com/gftp/history/src/gFTP.c">http://bb.caiguanhao.com/gftp/history/src/gFTP.c</a> 改成 <a href="http://bb.caiguanhao.com/gftp/history/gFTP.c">http://bb.caiguanhao.com/gftp/history/gFTP.c</a> 即可。</p>
<p><a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/bitbucket.png"><img class="alignnone size-full wp-image-189" title="bitbucket" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/bitbucket.png" alt="" width="530" height="311" /></a></p>
<p>Assembla中，浏览文件历史（Revision log）也没有提示，按Bitbucket的方法修改网址不行，网页这自动跳转到项目主页。只能通过changesets（即commits）里面查看。</p>
<p><a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/assembla.png"><img class="alignnone size-full wp-image-190" title="assembla" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/assembla.png" alt="" width="493" height="324" /></a></p>
<p>Unfuddle中，文件历史也没要提示移动过，但可以用修改网址的方法。如点击进入 History 后网址是 <a href="http://caiguanhao.unfuddle.com/a#/repositories/1/history?path=/src/gFTP.c&#038;commit=d2619b5080ffd862492aa275254872803fe40c88">http://caiguanhao.unfuddle.com/a#/repositories/1/history?path=/src/gFTP.c&#038;commit=d2619b5080ffd862492aa275254872803fe40c88</a>，看到这行的parent是dd9ebc604ebd074c94a0d2c0a72c673fc87a1908（即移动前的commit），复制，然后分别更改网址的path和commit，成为 <a href="http://caiguanhao.unfuddle.com/a#/repositories/1/history?path=/gFTP.c&#038;commit=dd9ebc604ebd074c94a0d2c0a72c673fc87a1908">http://caiguanhao.unfuddle.com/a#/repositories/1/history?path=/gFTP.c&#038;commit=dd9ebc604ebd074c94a0d2c0a72c673fc87a1908</a> 即可。</p>
<p><a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/unfuddle.png"><img src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/04/unfuddle-576x198.png" alt="" title="unfuddle" width="576" height="198" class="alignnone size-large wp-image-191" /></a></p>
<p>综合几个热门的免费存放点，GitHub比较人性化一点，当然最好一开始想好，first commit之后就不要移动了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/187/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP：读取lrc歌词</title>
		<link>http://www.caiguanhao.com/archives/184</link>
		<comments>http://www.caiguanhao.com/archives/184#comments</comments>
		<pubDate>Wed, 28 Mar 2012 03:03:19 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=184</guid>
		<description><![CDATA[SoundManager的一个360度player的其中一个演示是annotation(注释)，我就想到了这也可以是一个歌词的功能，于是就想到从lrc歌词转成annotation。lrc的一般格式是 [分:秒.百分之几秒] 歌词 [01:18.21]And who am I to judge if that&#8217;s all? 其中有些没有 .百分之几秒，另外也有一些是重复歌词的省略标记： [分:秒.百分之几秒][分:秒.百分之几秒][分:秒.百分之几秒]&#8230; 歌词 [03:13][03:10][02:07]I am a taxi driverman 首先是读取用file()逐行读取lrc歌词，然后用正则表达式匹配，要考虑没有百分秒的情况： /((\[(\d+):(\d+)(\.\d+)?\])+)(.*)/ 由于360度player不用百分秒，所以不用管百分秒；如果匹配结果 1 和 2 不相等表示同一歌词有多个时间（出现多次），跟着用 preg_match_all('/\[(\d+):(\d+)(\.\d+)?\]/', $matches[1], $submatches, PREG_SET_ORDER) 将所有时间列出来，逐一 array_push 加入到自己的数组中。这样多个时间同一歌词的项目将展开，逐一列出。 但问题是360度player只接受按顺序排列的annotation，于是就要对数组进行排序，用到 usort 自定义排序。也很简单，取出数组各项的时间的分和秒，然后分*60+秒计算总秒数，然后比较大小。 得到的数组可以用json方式到达网页jQuery读取，然后建成所需HTML结构，当然最后还要优化一下歌词，中文文件名就要用到iconv，trim歌词当然是少不了，空白行用“music&#8230;”代替等等。]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.schillmania.com/projects/soundmanager2/demo/360-player/canvas-visualization.html">SoundManager的一个360度player的其中一个演示</a>是annotation(注释)，我就想到了这也可以是一个歌词的功能，于是就想到从lrc歌词转成annotation。lrc的一般格式是</p>
<p>[分:秒.百分之几秒] 歌词<br />
[01:18.21]And who am I to judge if that&#8217;s all?</p>
<p>其中有些没有 .百分之几秒，另外也有一些是重复歌词的省略标记：</p>
<p>[分:秒.百分之几秒][分:秒.百分之几秒][分:秒.百分之几秒]&#8230; 歌词<br />
[03:13][03:10][02:07]I am a taxi driverman</p>
<p>首先是读取用<a href="http://php.net/manual/en/function.file.php">file()</a>逐行读取lrc歌词，然后用正则表达式匹配，要考虑没有百分秒的情况：</p>
<p>/((\[(\d+):(\d+)(\.\d+)?\])+)(.*)/</p>
<p>由于360度player不用百分秒，所以不用管百分秒；如果匹配结果 1 和 2 不相等表示同一歌词有多个时间（出现多次），跟着用 <code>preg_match_all('/\[(\d+):(\d+)(\.\d+)?\]/', $matches[1], $submatches, PREG_SET_ORDER)</code> 将所有时间列出来，逐一 array_push 加入到自己的数组中。这样多个时间同一歌词的项目将展开，逐一列出。</p>
<p>但问题是360度player只接受按顺序排列的annotation，于是就要对数组进行排序，用到 usort 自定义排序。也很简单，取出数组各项的时间的分和秒，然后分*60+秒计算总秒数，然后比较大小。</p>
<p>得到的数组可以用json方式到达网页jQuery读取，然后建成所需HTML结构，当然最后还要优化一下歌词，中文文件名就要用到iconv，trim歌词当然是少不了，空白行用“music&#8230;”代替等等。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/184/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C：使用macro循环关联数组</title>
		<link>http://www.caiguanhao.com/archives/178</link>
		<comments>http://www.caiguanhao.com/archives/178#comments</comments>
		<pubDate>Mon, 12 Mar 2012 15:26:01 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=178</guid>
		<description><![CDATA[TAGS: associative array key value loop 假设你的程序需要用到配置文件，开始时检测发现不存在，于是要基于默认值创建一个。当然我们都希望这些默认值都只出现一次，存放在一个数组之内，于是： 然后我们可以用 a.settingA 等来获取默认值。这个数组可以看作是一个关联数组（associative array）： settingA => &#8220;A&#8221; settingB => &#8220;B&#8221; 但是问题来了，当我想使用 GKeyFile 为配置文件设置值的时候，没有好像PHP的foreach之类的东西，该怎么办？我没有理由逐一用这个函数，这样太不优雅了，而且很多的时候怎么办？： 我这个时候Google一下然后看到了StackOverflow网站上有人解答了这个问题。那个问题还分了不同类型的，我下面介绍的则只有char类型的，方便理解： 到这里位置就已经初始化完毕。 如果后面需要循环整个数组就可以再定义 S 函数的内容，如： 当然，你可以上 StackOverflow 的 Is there any way to loop through a struct with elements of diferent types in C? 看一下，这种方法叫 X-Macro，当然 X 等等都可以换成自己喜欢的，熟练几次就学会了。]]></description>
			<content:encoded><![CDATA[<p>TAGS: associative array key value loop</p>
<p>假设你的程序需要用到配置文件，开始时检测发现不存在，于是要基于默认值创建一个。当然我们都希望这些默认值都只出现一次，存放在一个数组之内，于是：</p>
<pre class="brush: cpp; title: ; notranslate">
typedef struct {
	char *settingA;
	char *settingB;
} Settings;

Settings a = {&quot;A&quot;, &quot;B&quot;};
</pre>
<p>然后我们可以用 a.settingA 等来获取默认值。这个数组可以看作是一个关联数组（associative array）：</p>
<p>settingA => &#8220;A&#8221;</p>
<p>settingB => &#8220;B&#8221;</p>
<p>但是问题来了，当我想使用 GKeyFile 为配置文件设置值的时候，没有好像PHP的foreach之类的东西，该怎么办？我没有理由逐一用这个函数，这样太不优雅了，而且很多的时候怎么办？：</p>
<pre class="brush: cpp; title: ; notranslate">
g_key_file_set_string(keyfile, &quot;Main&quot;, &quot;settingA&quot;, &quot;A&quot;);
g_key_file_set_string(keyfile, &quot;Main&quot;, &quot;settingB&quot;, &quot;B&quot;);
...
</pre>
<p>我这个时候Google一下然后看到了StackOverflow网站上有人解答了这个问题。那个问题还分了不同类型的，我下面介绍的则只有char类型的，方便理解：</p>
<pre class="brush: cpp; title: ; notranslate">
// 为了美观和对应以后的输入，所以用换行的方法：
// 这里定义了 SETTINGS ，当用到这个macro的时候将会运行
// S(settingA) 和 S(settingB) 两个函数，你可以定义很多个
#define SETTINGS \
	S(settingA) \
	S(settingB)

// 到这里可以看出，你完全可以定义 S 函数的内容
// 于是在 struct 里面首先定义了 S 函数要做的是 char* name 其中name是第一个参数
// 通过引用之前的 SETTINGS，这样 settingA 和 settingB 都被定义为 char 类型
// 最后还要 undefine S函数
typedef struct {
	#define S(name) char* name;
	SETTINGS
	#undef S
} Settings;

// 设置默认值
Settings a = {&quot;A&quot;, &quot;B&quot;};
</pre>
<p>到这里位置就已经初始化完毕。</p>
<p>如果后面需要循环整个数组就可以再定义 S 函数的内容，如：</p>
<pre class="brush: cpp; title: ; notranslate">
// 将默认值写成keyfile，然后写到文件：
#define S(name) g_key_file_set_string(keyfile, &quot;Main&quot;, #name, a.name);
SETTINGS
#undef S
g_file_set_contents(&quot;set.conf&quot;,
	g_key_file_to_data(keyfile, NULL, NULL), -1, NULL);

// 或读取配置文件的值，然后改写默认值：
#define S(name) a.name=g_key_file_get_string(keyfile, &quot;Main&quot;, #name, NULL);
SETTINGS
#undef S
</pre>
<p>当然，你可以上 StackOverflow 的 <a href="http://stackoverflow.com/questions/1784782/is-there-any-way-to-loop-through-a-struct-with-elements-of-diferent-types-in-c/1785699">Is there any way to loop through a struct with elements of diferent types in C?</a> 看一下，这种方法叫 X-Macro，当然 X 等等都可以换成自己喜欢的，熟练几次就学会了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/178/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C：一个不经意的错误</title>
		<link>http://www.caiguanhao.com/archives/174</link>
		<comments>http://www.caiguanhao.com/archives/174#comments</comments>
		<pubDate>Sat, 18 Feb 2012 11:20:43 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=174</guid>
		<description><![CDATA[在配置curl时因为贪方便犯了一个不经意的错误，用curl下载图片，然后写入某个文件，但是下载完毕后，文件还不完整（图片末端未保存，看到黑边），直至程序关闭才显示完整。期间怀疑过是thread、curl的cleanup、gtk_main_quit等等的问题，最后终于找到是fopen没有fclose，因为偷懒。 不完整的图片文件（文件大小较小，末端有黑边）： 完整的图片文件： 偷懒的代码： 这才没问题： fclose不同curl_easy_cleanup那些的一点是，当fclose了一个不存在的东西就会出现严重错误。 另外有一些如果不简化将会显得很累赘： 简化为： Keywords: C libcurl incomplete download]]></description>
			<content:encoded><![CDATA[<p>在配置curl时因为贪方便犯了一个不经意的错误，用curl下载图片，然后写入某个文件，但是下载完毕后，文件还不完整（图片末端未保存，看到黑边），直至程序关闭才显示完整。期间怀疑过是thread、curl的cleanup、gtk_main_quit等等的问题，最后终于找到是fopen没有fclose，因为偷懒。</p>
<p>不完整的图片文件（文件大小较小，末端有黑边）：<br />
<a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/02/incomplete.png"><img class="alignnone size-full wp-image-176" title="incomplete" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/02/incomplete.png" alt="" width="222" height="124" /></a></p>
<p>完整的图片文件：<br />
<a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/02/complete.png"><img class="alignnone size-full wp-image-175" title="complete" src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/02/complete.png" alt="" width="222" height="124" /></a></p>
<p>偷懒的代码：</p>
<pre class="brush: cpp; title: ; notranslate">
...
curl_easy_setopt(http_handle[i], CURLOPT_WRITEDATA, fopen(filename, &quot;wb&quot;));
...
</pre>
<p>这才没问题：</p>
<pre class="brush: cpp; title: ; notranslate">
...
CURL *http_handle[items_found];
FILE *fp[items_found];
...
while ( ... ) {
	...
	fp[i]=fopen(filename,&quot;wb&quot;);

	http_handle[i] = curl_easy_init();
	...
	curl_easy_setopt(http_handle[i], CURLOPT_WRITEFUNCTION, write_data);
	curl_easy_setopt(http_handle[i], CURLOPT_WRITEDATA, fp[i]);
	...
}
...
for(i=0;i&lt;items_found;i++){
	curl_easy_cleanup(http_handle[i]);
	fclose(fp[i]); //如果不用，直到关闭程序后才会close
}
...
</pre>
<p>fclose不同curl_easy_cleanup那些的一点是，当fclose了一个不存在的东西就会出现严重错误。</p>
<p>另外有一些如果不简化将会显得很累赘：</p>
<pre class="brush: cpp; title: ; notranslate">
size_t write_data (void *ptr, size_t size, size_t nmemb, FILE *stream) {
	size_t written;
	written = fwrite(ptr, size, nmemb, stream);
	return written;
}
</pre>
<p>简化为：</p>
<pre class="brush: cpp; title: ; notranslate">
size_t write_data (void *ptr, size_t size, size_t nmemb, FILE *stream) {
	return fwrite(ptr, size, nmemb, stream);
}
</pre>
<p>Keywords: C libcurl incomplete download</p>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/174/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C：Linux下解决curl太慢（等15秒）的办法</title>
		<link>http://www.caiguanhao.com/archives/170</link>
		<comments>http://www.caiguanhao.com/archives/170#comments</comments>
		<pubDate>Sun, 12 Feb 2012 06:01:35 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[curl]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=170</guid>
		<description><![CDATA[Linux（如Ubuntu）下的curl有时会很慢，每次请求都要等待约15秒后才有结果出来，排除了网速慢的原因，很可能是电信DNS解析或者是路由器的问题。网上比较多的做法是设置Header的Expect为空，但这种做法貌似没什么作用，还有一种是设置使用IPV4 curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); ，可以在终端输入 time curl -4 http://www.caiguanhao.com/custom-homepage/weather/daliang.json 测试以下速度，如果之前是15秒才出结果，用了IPV4应该只需要约5秒的时间。]]></description>
			<content:encoded><![CDATA[<p>Linux（如Ubuntu）下的curl有时会很慢，每次请求都要等待约15秒后才有结果出来，排除了网速慢的原因，很可能是电信DNS解析或者是路由器的问题。网上比较多的做法是设置Header的Expect为空，但这种做法貌似没什么作用，还有一种是设置使用IPV4 <code>curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);</code> ，可以在终端输入 <code>time curl -4 http://www.caiguanhao.com/custom-homepage/weather/daliang.json</code> 测试以下速度，如果之前是15秒才出结果，用了IPV4应该只需要约5秒的时间。</p>
<pre class="brush: cpp; title: ; notranslate">
#include &lt;curl/curl.h&gt;

...

CURL *curl_handle = NULL;
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, url);

...

curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);

...

struct curl_slist *slist=NULL;
slist = curl_slist_append(slist, &quot;Expect:&quot;);
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, slist);

curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/170/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JS+PHP：LastPass的密码强度算法</title>
		<link>http://www.caiguanhao.com/archives/166</link>
		<comments>http://www.caiguanhao.com/archives/166#comments</comments>
		<pubDate>Tue, 07 Feb 2012 16:48:54 +0000</pubDate>
		<dc:creator>caiguanhao</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.caiguanhao.com/?p=166</guid>
		<description><![CDATA[有时需要在网页上验证用户输入的密码的强度，而相类似的PHP代码可用于后台密码强度统计。在Chrome上安装LastPass，然后在Preferences>Extensions>勾选Developer Mode>点击LastPass>点击Inspect active views: background.html，然后在Resources>Scripts>lpfulllib.js下找到这段JS代码。 转为相类似的PHP函数：]]></description>
			<content:encoded><![CDATA[<p>有时需要在网页上验证用户输入的密码的强度，而相类似的PHP代码可用于后台密码强度统计。在Chrome上安装LastPass，然后在Preferences>Extensions>勾选Developer Mode>点击LastPass>点击Inspect active views: background.html，然后在Resources>Scripts>lpfulllib.js下找到这段JS代码。</p>
<p><a href="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/02/lasspass-generate-password.png"><img src="http://www.caiguanhao.com/thewordpress/wp-content/uploads/2012/02/lasspass-generate-password.png" alt="" title="lasspass-generate-password" width="289" height="322" class="alignnone size-full wp-image-167" /></a></p>
<pre class="brush: jscript; title: ; notranslate">
function getpasswordstrength(a, b) {
	var c = 0;
	if (a == &quot;&quot; &amp;&amp; b == &quot;&quot;) return 0;
	if (b == a) return 1;
	if (a != &quot;&quot; &amp;&amp; a.indexOf(b) != -1) c -= 15;
	if (a != &quot;&quot; &amp;&amp; b.indexOf(a) != -1) c -= a.length;
	c += b.length;
	if (b.length &gt; 0 &amp;&amp; b.length &lt;= 4) c += b.length;
	else if (b.length &gt;= 5 &amp;&amp; b.length &lt;= 7) c += 6;
	else if (b.length &gt;= 8 &amp;&amp; b.length &lt;= 15) c += 12;
	else if (b.length &gt;= 16) c += 18;
	if (b.match(/[a-z]/)) c += 1;
	if (b.match(/[A-Z]/)) c += 5;
	if (b.match(/\d/)) c += 5;
	if (b.match(/.*\d.*\d.*\d/)) c += 5;
	if (b.match(/[!,@,#,$,%,^,&amp;,*,?,_,~]/)) c += 5;
	if (b.match(/.*[!,@,#,$,%,^,&amp;,*,?,_,~].*[!,@,#,$,%,^,&amp;,*,?,_,~]/)) c += 5;
	if (b.match(/(?=.*[a-z])(?=.*[A-Z])/)) c += 2;
	if (b.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)) c += 2;
	if (b.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&amp;,*,?,_,~])/)) c += 2;
	var d = [],
		e = 0,
		f, h;
	for (f = 0; f &lt; b.length; ++f) {
		h = b.charAt(f);
		if (typeof d[h] == &quot;undefined&quot;) {
			d[h] = 1;
			++e
		}
	}
	if (e == 1) return 2;
	c *= 2;
	if (c &lt; 0) c = 0;
	else if (c &gt; 100) c = 100;
	return c
}
</pre>
<p>转为相类似的PHP函数：</p>
<pre class="brush: php; title: ; notranslate">
function get_password_strength($a=null,$b){
	$c=strlen($b);
	if(isset($b[0])&amp;&amp;strlen(str_replace($b[0],'',$b))==0)return 2;
	if($a==''&amp;&amp;$b=='')return 0;
	if($a==$b)return 1;
	if($a!=''&amp;&amp;strpos($a,$b)!==false)$c-=15;
	if($a!=''&amp;&amp;strpos($b,$a)!==false)$c-=strlen($a);
	if(strlen($b)&gt;0&amp;&amp;strlen($b)&lt;=4)$c+=strlen($b);
	elseif(strlen($b)&gt;=5&amp;&amp;strlen($b)&lt;=7)$c+=6;
	elseif(strlen($b)&gt;=8&amp;&amp;strlen($b)&lt;=15)$c+=12;
	elseif(strlen($b)&gt;=16)$c+=18;
	if(preg_match('/[a-z]/',$b))$c+=1;
	if(preg_match('/[A-Z]/',$b))$c+=5;
	if(preg_match('/\d/',$b))$c+=5;
	if(preg_match('/.*\d.*\d.*\d/',$b))$c+=5;
	if(preg_match('/[!,@,#,$,%,^,&amp;,*,?,_,~]/',$b))$c+=5;
	if(preg_match('/.*[!,@,#,$,%,^,&amp;,*,?,_,~].*[!,@,#,$,%,^,&amp;,*,?,_,~]/',$b))$c+=5;
	if(preg_match('/(?=.*[a-z])(?=.*[A-Z])/',$b))$c+=2;
	if(preg_match('/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/',$b))$c+=2;
	if(preg_match('/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&amp;,*,?,_,~])/',$b))$c+=2;
	$c*=2;
	if($c&lt;0)$c=0;elseif($c&gt;100)$c=100;
	return $c;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.caiguanhao.com/archives/166/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

