1
6
2011
14

编译了 Vim7.3.98 for Win32

vim_dev邮件列表看到Bram又发布了好些补丁,想起不久前看到梧桐在找支持Ruby的Vim二进制文件,所以我又编译了下Vim。上次编译Win版时Lua好不容易弄好了,Ruby却不能用。有些相关的补丁发出来了,这次也尝试下,欣喜地发现这个bug已经解决了~

对了,我尝试过使用Make_cyg.mak在Cygwin环境下编译,结果失败了,好像是gcc不支持这么编译了,所以还是用的MinGW。

文件放dbank网盘了,外链地址。因为有些涉及到文档什么的补丁,所以runtime文件也打包传了上去。另外说下,dbank虽然上传文件速度很快,但操作界面的时候,网速却极慢(用HttpFox看过了,不是JS导致的慢)。

注:支持的Python版本是2.7,Ruby是1.9.2,Lua还是静态编译的,5.2。


2011年3月29日更新:

把自己编译的 Windows 版 Vim 放这里了,会不时更新。

Category: Vim | Tags: vim windows
1
1
2011
47

让Vim在图形界面与终端中的Alt组合键相同

首先祝大家新年快乐!


一直都感觉Vim下快捷键不够用,于是在某一天,我开始使用Alt开头的组合键,然后发现了问题——

在很多终端中,Alt 组合键发送的是 Esc 前缀键码,而图形界面中则是置位最高位。举例来说,Alt-x在图形界面下向Vim发送的是ø(在Vim插入模式下使用Ctrl-V Alt-x可以看到),其编码为0xf1,而x的编码为0x78,区别在于前者二进制编码的最高位是 1,而后者是 0。

而在gnome-terminal、konsole中则是另外一番景象。Alt-x和快速地按Esc x的效果是一样的,仅有xterm 和 rxvt 等终端可选地支持像图形界面的那样处理(参见Vim手册:help :map-alt-keys)。而且,使用置位最高位的终端将导致shell中的Alt-f之类的键绑定失效。

Emacs能处理这种不一致,但Vim不能,于是我一直是使用脚本,使得在终端下和图形界面下使用不同的键绑定。这样图形界面下没什么问题,但终端下比较郁闷:因为映射了Esc开头的键,而Esc是用于回到普通模式的,于是每次按Esc想退回到普通模式时都得等一秒('timeoutlen的值)。这个值又不能设小,不然\ww这种需要多次按键的映射就难用了。

前些天,偶然在帮助文档里看到了这个:

					*:set-termcap* *E522*
需要 {option} 的地方,可以使用 "t_xx" 形式来设置终端选项。这些选项覆盖相应的
termcap 值。设置后,可以用于映射。如果 "xx" 包含特殊字符,须用 <t_xx> 形式: >
	:set <t_#4>=^[Ot
也可用来翻译普通键的特殊键码。例如,如果 Alt-b 产生 <esc>b,可用: >
	:set <m-b>=^[b
(这里 ^[ 是真正的 <esc>,用 CTRL-V <esc> 来输入)
这个方法优于映射之处在于它能适用于所有情况。

也就是说,可以在终端下把Alt组合键都设置到Esc开头的键码,这样一是不用每次设置键映射时设置两个,更重要的是,其本质变了:这样的设置不是键映射,而是指定键码!这样会使用'ttimeoutlen'的值来等待后续键码,和映射无关了,我完全可以把它设置得很小。于是写出新的脚本:

 1 " escalt.vim    控制台下让用 <M-x> 也可用
 2 " Author:       lilydjwg <lilydjwgATgmail.com>
 3 " Last Change:  2010年12月15日
 4 " ----------------------------------------------------------
 5 " Load Once:
 6 if &cp || exists("g:loaded_escalt") || has("gui_running")
 7   finish
 8 endif
 9 let s:keepcpo = &cpo
10 let g:loaded_escalt = 1
11 set cpo&vim
12 " ----------------------------------------------------------
13 " Functions:
14 function Escalt_console()
15   for i in range(65, 90) + range(97, 122)
16     exe "set <M-".nr2char(i).">=\<Esc>".nr2char(i)
17   endfor
18   set ttimeoutlen=50
19   if &term =~ 'xterm'
20     set <F1>=^[OP
21     set <F2>=^[OQ
22     set <F3>=^[OR
23     set <F4>=^[OS
24     set <Home>=^[OH
25     set <End>=^[OF
26   endif
27   for i in ["", "c", "i", "x"]
28     exe i . "map Ï1;2P <S-F1>"
29     exe i . "map Ï1;2Q <S-F2>"
30     exe i . "map Ï1;2R <S-F3>"
31     exe i . "map Ï1;2S <S-F4>"
32   endfor
33 endfunction
34 " ----------------------------------------------------------
35 " Call Functions:
36 call Escalt_console()
37 " ----------------------------------------------------------
38 "  Restoration And Modelines:
39 let &cpo= s:keepcpo
40 unlet s:keepcpo
41 " vim:fdm=expr:fde=getline(v\:lnum-1)=~'\\v"\\s*-{20,}'?'>1'\:1

注意到其中对于F1到F4等键进行了特殊的设置。没办法,这几个键特殊,这样设置我觉得是最优的解了。设置'ttybuiltin'也可以,但是经过一些时间的试用后发现有副作用,具体是什么我忘记了。


PS: SyntaxHighlighter 不支持 Vimscript,还好 Vim 有TOhtml命令。

最新脚本在 GitHub 有。直接复制上面高亮过的代码是不行的。

Category: Vim | Tags: linux vim
12
15
2010
4

从UDP到解决DNS污染的脚本

最近做网络课的实验,涉及UDP协议。因为UDP协议比TCP用得少,所以我以前没试过创建几个UDP的socket。现在忽然有了兴致,就试了试。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

socket创建好了,往哪里发消息呢?我想到了DNS。首先要有报文。没找到容易上手的Python库,就Google到了这个,DNS报文的格式。没功夫细细研究,先弄个报文测试下:

q = b'>:\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07twitter\x03com\x00\x00\x01\x00\x01'
s.sendto(q, ('8.8.8.8', 53))

\x07twitter\x03com\x00就是要查询的域名了,我想测试什么,明白人都看得出来了。接下来接收回答,接收了多次:

>>> '.'.join(str(int(i)) for i in s.recv(4096)[-4:])
'46.82.174.68'
>>> '.'.join(str(int(i)) for i in s.recv(4096)[-4:])
'128.121.146.100'

再接收的话就阻塞了。对于普通域名,当然是最多只会接收到一次的啦。如果报文出错的话(比如我不小心少写了最后的\x00\x01),是收不到回答的。

有人依据此现象写了个pydnsproxy 放在Google Code上。主页说“暂不公布方法”,但其实很简单。下面是我整理过的代码,转成了Python3的语法:

#!/usr/bin/env python3
# vim:fileencoding=utf-8

from socketserver import *
from socket import *
import sys, os

'''
来源 http://code.google.com/p/pydnsproxy/
'''

DEF_LOCAL_HOST = '127.0.0.1'
DEF_REMOTE_SERVER = '8.8.8.8'
DEF_PORT = 5350
DEF_CONF_FILE = 'dnsserver.conf'
DEF_TIMEOUT = 0.4

gl_remote_server = None

class LocalDNSHandler(BaseRequestHandler):
  def setup(self):
    global gl_remote_server
    if not gl_remote_server:
      remote_server = DEF_REMOTE_SERVER
    else:
      remote_server = gl_remote_server
    self.dnsserver = (remote_server, 53)

  def handle(self):
    data, socket = self.request
    rspdata = self._getResponse(data)
    socket.sendto(rspdata, self.client_address)

  def _getResponse(self, data):
    "Send client's DNS request (data) to remote DNS server, and return its response."
    sock = socket(AF_INET, SOCK_DGRAM) # socket for the remote DNS server
    sock.sendto(data, self.dnsserver)
    sock.settimeout(5)
    while True:
      try:
        rspdata = sock.recv(4096)
        break
      except error as e:
        if e.errno != 11:
          raise
        else:
          print("Try again")
    # "delicious food" for GFW:
    while True:
      sock.settimeout(DEF_TIMEOUT)
      try:
        rspdata = sock.recv(4096)
        print("GFWed?")
      except timeout:
        break
      except error as e:
        if e.errno != 11:
          raise
        else:
          print("Trying again")
    return rspdata

class LocalDNSServer(ThreadingUDPServer):
  pass

def main():
  global gl_remote_server
  try:
    if hasattr(sys, 'frozen'):
      dir = os.path.dirname(sys.executable)
    else:
      dir = os.path.dirname(__file__)
    confFile = os.path.join(dir, DEF_CONF_FILE)
    f = open(confFile, 'r')
    dns = f.read().split('=')
    f.close()
    if len(dns) == 2:
      if dns[0].strip().lower() == 'dns':
        gl_remote_server = dns[1].strip()
      else:
        pass
  except:
    pass
  dnsserver = LocalDNSServer((DEF_LOCAL_HOST, DEF_PORT), LocalDNSHandler)
  dnsserver.serve_forever()

if __name__ == '__main__':
  main()

注意到和原程序不同的是,我捕获了错误号为11的socket.error异常。这个是EAGAIN,“资源临时不可用”,只会在设置了超时后出现。man文档recv(2)对此的解释是:

EAGAIN or EWOULDBLOCK
       The socket is marked  nonblocking  and  the  receive  operation
       would  block, or a receive timeout had been set and the timeout
       expired before data was received.  POSIX.1-2001  allows  either
       error  to be returned for this case, and does not require these
       constants to have the same value,  so  a  portable  application
       should check for both possibilities.

不知道是怎么回事,再次接收却又可以收到数据。也许正如其名,是期待调用者再次尝试吧。我越来越觉得,Python 的异常处理里应该有tryagain这样的语句了。

2012年11月10日更新:更好地摆脱 DNS 污染,请参考此文

Category: 网络 | Tags: DNS python 网络 UDP
11
29
2010
0

snipMate表达式扩展补丁

snipMate 是Vim的片断扩展插件之一,介绍网上很多,实在不想Google的话可以看看这里,我就不多言了。

我经常会把一些网站链接连同其标题写到自己的wiki或者邮件中,于是有个这个snippet:

snippet link
	[`@*` `system('getTitle '''.@*.'''')`]${1}

扩展 * 寄存器(也就是X选择区)中的URL,并使用getTitle这个我自己用Python写的程序获取标题。这个snippet大多数情况下工作良好,只是偶尔会使Vim停止响应,只能Ctrl-C中止。今天在扩展 http://larrupingpig.zoka.cc/?p=197 这个URL时又遇到这个问题,实在受不了了,于是自己看源代码。`...`的扩展在这里:

	if stridx(snippet, '`') != -1
		while match(snippet, '`.\{-}`') != -1
			let snippet = substitute(snippet, '`.\{-}`',
						\ substitute(eval(matchstr(snippet, '`\zs.\{-}\ze`')),
						\ "\n\\%$", '', ''), '')
		endw
		let snippet = substitute(snippet, "\r", "\n", 'g')
	endif

问题就出在substitute中的substitute上。:help substitute()可以看到,其第三个参数中有些特殊字符,比如前边那个网页标题中的&,会被特殊对待。

知道问题出在哪儿了,也就有改的办法了,我在Google Code上报告了bug,并提交了补丁。但看看那么多的issue和Github上的Pull Request,我感觉被采纳的可能性好小啊。。。

Category: Vim | Tags: vim
11
26
2010
8

在Win编译带Lua支持的GVIM

虽然已经在Linux下待了这么长时间,可是,还是有很多时候,我不得不面对难用的Windows。而在Linux下一直自己编译Vim的我,今天在得知刘春棍的行动的支持下,终于鼓足了勇气,下载安装MinGW,编译了GVIM。

在Windows下编译GVIM和Linux下不同,不能用./configure来配置,要到src目录下,修改这下面的Make_ming.mak文件,改好后直接make -f Make_ming.mak编译。

Win下编译东西花的时间长就不说了。我编译了好几遍。Python支持没问题,设定好路径就OK,Ruby我是为编译GVIM专门装的,除了静态编译时会报错外,还有个很严重的问题,导致我最后放弃了+ruby。这个后面再说。Lua我下的是最新的5.2版,一个lua5_2_work2_Win32_bin.zip包含了exe和dll文件,另一个lua5_2_work2_Win32_mingw4_lib.zip包含了头文件和一个 .a 文件。我最开始以为和ruby一样只能动态载入,于是设置了DYNAMIC_LUA=yes,结果编译是成功了,但载入失败:

luaL_typerror无法加载

我用cg搜索了下,luaL_typerror被 #define 成了luaL_typeerror,这看来是5.2改的。我手动在if_lua.c里把这个给改了,结果发现,还有其它函数也是类似的情况,比如lua_call等。于是我尝试静态编进去试试,竟然成功了!

然而,喜悦总是短暂的。我以前只知道ruby支持有问题,没想到这次问题更严重了,:edit命令都用不了:

当然,一开始我还不能肯定是ruby支持的问题,但当编译了不带ruby支持的版本后,这个奇异的问题就没有了。

Vim7.3对其他语言的支持的问题真是多啊:Python3的中文支持有问题,ruby的支持也有问题,要支持Lua5.2还得自己改源代码。。。

不管怎么说,Lua静态编译进去了我还是很高兴的。虽然在没装Lua的机器上没那些Lua的库的,但至少在Vimscript外又多了一种可用的语言了。

最后,想下载我编译的 Windows 32 版 vim 的请到这里来。

Category: Vim | Tags: vim windows Lua
11
21
2010
0

Your Google Account

Gmail 钓鱼邮件

今收到的一个失败的钓鱼邮件。

PS:为什么Gmail没有提示此邮件可能存在欺诈什么的呢?

Category: 网络 | Tags: 安全 google
11
7
2010
5

编译Awesome时出现module 'luadoc' not found错误的解决

今天在Arch上安装awesome,不料官方源里没有,说是因为需要cairo-xcb这个包,但它也不在源里。郁闷地使用yaourt安装。依赖N多东西。过程中竟然要卸载我的finch啊什么的好多包。果断按下N键。重新yaourt。依赖安好了,才注意到awesome使用的是cmake。这东西我在尝试csync时用过。

cmake到一半,停下来了:

[ 61%] Built target generated_icons
lua: /usr/bin/luadoc:7: module 'luadoc' not found:
	no field package.preload['luadoc']
	no file '/home/lilydjwg/scripts/lua/luadoc.lua'
	no file './luadoc.so'
	no file '/usr/lib/lua/5.1/luadoc.so'
	no file '/usr/lib/lua/5.1/loadall.so'
stack traceback:
	[C]: in function 'require'
	/usr/bin/luadoc:7: in main chunk
	[C]: ?
make[3]: *** [CMakeFiles/luadoc] 错误 1
make[2]: *** [CMakeFiles/luadoc.dir/all] 错误 2
make[1]: *** [all] 错误 2
make: *** [cmake-build] 错误 2
    正在放弃...
==> ERROR: Makepkg was unable to build awesome.

刚刚明明装了luadoc的,现在竟然没找到。。。再仔细一看,坏了,是luadoc没找到它自己的模块。。。我对lua可不比python,可以说除了那种学过编程的人都能看懂的部分外其它的完全不懂。Google过,完全没有我要找的东西。也在Ubuntu下装了luadoc,结果一样的问题。在《Lua程序设计》里看了下lua找模块的方法,然后只好胡乱尝试设置LUA_PATH变量了。辛苦的试错过程就不多说,找到的解决办法是在PKGBUILDcmake前边加上

export LUA_PATH='/usr/share/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua'

随后编译成功。太晚了,不测试了,先睡觉去。

Category: Linux | Tags: arch awesome Lua
11
6
2010
2

privoxy与火狐智能地址栏两者兼得

privoxy是一款强大的代理调度器,看到muzuiget同学的介绍我就心动了,由于比较忙所以前两天才开始尝试,很快就发现了一个问题:直接在火狐地址栏里输入关键字会转到privoxy的“找不到域名”的错误页。

依我的推测,火狐总是尝试解析用户在地址栏输入的字符串,当解析失败时就尝试添加.com这样的后缀,然后尝试向Google查询,或者返回一个最接近的结果,或者返回Google搜索页面。(所以,我非常讨厌垃圾电信的114广告,使得任何原本不能解析的域名都解析到它的广告页了。)

在使用HTTP代理之后,火狐无法得知域名解析是否成功,因为代理在出错时返回了出错页面。这让我这个重试依赖她的地址栏的火狐迷郁闷了一天,最终(部分地)解决了此问题。

privoxy果然强大,出错页都是可以自定义的,默认这些模板在配置目录的templates目录下,很容易猜到处理域名解析失败的文件是no-such-domain。于是我在改了下其外观的同时,先是加上搜索功能:

<p>
    This is often a temporary failure, so you might just
    <a href="@protocol@@hostport@@path@">try again</a>,
    or <a href="https://encrypted.google.com/search?hl=zh-CN&q=@hostport@">Google @host@</a>.
</p>

这样还不行,不光得点一下,而且只能跳转到Google的搜索页。想到Google有个“手气不错”按钮,于是利用之:

var abar = function(){
  if(document.referrer) return;
  var host = "@host@";
  if(host.indexOf('.')<0 || host.indexOf(' ')>-1){
    document.title = "跳转中...";
    window.location = "https://encrypted.google.com/search?hl=zh-CN&q="+host+"&btnI=1";
  }
};
abar();

将这段代码加到<head>后即可实现输入关键字后自动使用Google的“手气不错”服务跳转到接近的页面,或者是Google搜索页面。

但测试几次后发现,这样还有问题:如果输入中文,将搜索其punycode而不是中文了。比如在地址栏输入“测试”,回车之后会搜索“xn--0zwm56d”,进而跳转到http://www.iana.org/domains/root/db/xn--0zwm56d.html。看来需要用Javascript解punycode了。Google下,在stackoverflow看到了一个实现,还是放弃版权的~只是我没发现怎么才能让privoxy的错误页调用本地的外部Javascript文件,所以直接把代码嵌进去了,这让我这个喜欢代码整洁的人有点不爽。。。

var abar = function(){
  if(document.referrer) return;
  var host = punycode.ToUnicode("@host@");
  if(host.indexOf('.')<0 || host.indexOf(' ')>-1){
    document.title = "跳转中...";
    window.location = "https://encrypted.google.com/search?hl=zh-CN&q="+host+"&btnI=1";
  }
};
abar();

按说这样的设计是跨浏览器的,于是我测试了下Google Chrome,发现其并不买帐,输入看起来不像域名的字符串后,Chrome直接进到搜索页面。。。。还是火狐好啊~~

Category: 火狐 | Tags: javascript 火狐 privoxy
10
29
2010
7

金山快盘之跨平台同步脚本

金山快盘是和Dropbox差不多的同步服务,区别只是快盘(目前)只支持Windows,容量只有2G多(我的Dropbox都4G多了~),安全性不好(网页版的使用不加密的HTTP协议)、同步文件夹被设为系统属性等等。

由于某些原因,同学和我通过金山快盘分享文件。于是开始研究之。客户端上传下载时使用XML格式,但数据都加密过了,无解。于是转攻网页版。基本上都是很简单的json,上传使用了个Flash,用Wireshark抓包一看还是HTTP POST。

于是ksync诞生了,仅支持从服务器到本机的单向共享文件同步。如有其它需求可自行扩展。本程序仅需要Python3支持,目前仅在Ubuntu Linux上测试通过。

下载链接。使用前记得编辑ksync文件,把自己的用户名、cookie文件存放路径、同步目标路径替换掉。

Category: python | Tags: linux python json
10
24
2010
4

把Parted Magic装到移动硬盘

首先,不知道Parted Magic为何物的童鞋请先移步这里

之前折腾了Gparted live,然后看到了muzuiget童鞋的留言,决定有时间还是折腾下。首先去官网,结果官网说

As of Parted Magic version 5.3 the USB zip has been discontinued. We are only supporting Unetbootin as the method for booting Parted Magic from a USB drive.

Note: If you still want to do it manually, extract the files from the ISO image and execute the syslinux commands. If you know what you are doing this shouldn't be an issue.

好吧,我在看到第一段,在失望之余,“note”到了后面那段。

fuseiso ~/dl/pmagic-5.5.iso pm

我不喜欢需要根用户权限的mount,所以有一天在新立得里翻到了这个fuseiso,现在用上了。

里边就两个目录,boot和pmagic。pmagic比较简单,先说说它。

它下面也就三项,内核bzImage,initrd文件initramfs,和一个可以用来自定义其模块的目录pmodules,目前只包含pmagic-5.5.sqfs,file之可以知道它是一个Squashfs filesystem。

boot下东西有点多,还好我需要的很少,就是boot/isolinux/isolinux.cfg这个isolinux的配置文件了,里面有需要传给内核的参数,有好几个选项,基本上是这样子的:

LINUX /pmagic/bzImage
INITRD /pmagic/initramfs
APPEND edd=off noapic load_ramdisk=1 prompt_ramdisk=0 rw vga=788 loglevel=0 max_loop=256

APPEND那行就是内核的参数了。于是,grub2的参数应该这么写:

menuentry "Parted Magic (中文)" {
	set root="(hd1,1)"
	echo "Loading kernel..."
	linux /boot/pmagic/bzImage edd=off noapic load_ramdisk=1 prompt_ramdisk=0 rw vga=791 loglevel=0 max_loop=256 keymap=us zh_CN
	echo "Loading initrd..."
	initrd /boot/pmagic/initramfs
}

vga参数我自然要改下的,呵呵。

但是——

内核加载完毕之后它就告诉我找不到pmagic-5.5.sqfs文件……看来需要把它放到分区的/pmagic/pmodules下才行啊。可是我又不是做LiveUSB,是移动硬盘耶,不可能随随便便就把整个分区弄乱的。

于是——

file一下那个initramfs可知它是gzip压缩过的。

zcat initramfs > ../../initrd

然后继续file之,cpio文件。那就继续解包吧:

cat initrd|cpio -i

其实两步是可以一起做的。但没有file以及man cpio之前谁知道呢。

然后我很惊奇地看到一个名为init的可执行文件躺在当前目录下!我都没file,直接把它扔给Vim了。接下来就是看shell脚本了。

find_pmagic () {

   if [ ! -e /test_mnt/${directory}/pmagic/pmodules/pmagic-$VERSION.sqfs ]; then
      umount /test_mnt &> /dev/null
      return
   fi

关键是${directory}变量。继续寻找。。。

for i in $(cat /proc/cmdline); do
   case $i in
      directory=*)     directory=$(get_opt $i) ;;
      iso_location=*)  iso_location=$(get_opt $i)  ;;
      iso_filename=*)  ISO_VERSION=$(get_opt $i) ;;
      root=*)          root=$(get_opt $i)  ;;
      label=*)         label=$(get_opt $i)  ;;
      uuid=*)          uuid=$(get_opt $i) ;;
   esac
done

OK,原来是内核参数,于是在linux那行后面再加上directory=other,重启OK!

部分静态文件存储由又拍云存储提供。 | Theme: Aeros 2.0 by TheBuckmaker.com