消除 koomail 的“余孽”

2009/08/27 | 22:55 | 分类:Windows应用 | 标签: | 474次阅读

  koomail 曾是一个简洁小巧的 Windows 邮件客户端,是抵制 Foxmail 的良好选择,我使用它大约有两年了。但自从某个版本开始,koomail 界面变得花哨,不断增加一些华而不实的功能,还在主界面弄出了“山寨酷友”之类的 SNS 广告。相反地,我发现并上报的一些 bugs(涉及远程邮箱管理、RSS 阅读等)它却迟迟没有修复;我在其论坛提出的一些建议(诸如回复邮件时给原文前加上“>”这样的基本功能)也没有得到过作者的重视。于是,今天决定抛弃 koomail,在 Wikipedia 上另找一些优秀的邮件客户端。
  在研究这些客户端的过程中,发现老外们整出来的 mboxMaildirMH 等一系列邮件存储格式还是比较有用的。使用这些规范的格式毕竟有助于软件更替和数据移植,因此格式支持成为我考虑的重要因素。在将 koomail 的邮件导入其它客户端时,我发现部分邮件会被报告 MIME 错误,或者只能看到邮件头而看不到正文。经查这是因为 koomail 保存邮件时,将 SMTP(RFC 2821)中表示通信结束的“.”也存入了邮件文件,还在后面追加了一行不知做什么用的“k[0x01]m”。而 mbox 等格式规定文件只需存储 RFC 2822 消息本身。明白了原因,就可以写一个 bash 脚本来处理所有的邮件,消除 koomail 留下的两行“余孽”,让其它客户端可以正常读取:

  1. #!/bin/bash
  2.  
  3. for file in *; do
  4.     echo "$file"
  5.     sed -i 's/^k\x01m\x0d$/\x0d/g' "$file"
  6.     sed -i 's/^\.\x0d$/\x0d/g' "$file"
  7. done

  这时使用 Claws-Mail 客户端读入邮件(因为它和 koomail 一样,每封邮件是一个独立的文件),导出为 mbox 之后再在 Thunderbird 等其它客户端中阅读也是正常的。

在 shell 下区分 stdout 和 stderr 的两种思路

2009/08/07 | 15:19 | 分类:Linux与开源 | 标签: | 869次阅读

  1.使用命名管道重定向。在一个终端中新建命名管道,将标准错误重定向到此管道文件;在另一个终端中读取管道输出:

  1. linjian@goslin:~/dev/outerr$ cat ./test1.sh
  2. #!/bin/bash
  3.  
  4. while true; do
  5.     echo "Input: "
  6.     read FOO
  7.     echo "I am in stdout, $FOO"
  8.     echo "I am in stderr, $FOO" >&2
  9. done
  10. linjian@goslin:~/dev/outerr$ mkfifo mypipe
  11. linjian@goslin:~/dev/outerr$ ./test1.sh 2> ./mypipe
  12. Input:
  13. Hello World
  14. I am in stdout, Hello World
  15. Input:
  1. linjian@goslin:~/dev/outerr$ cat ./mypipe
  2. I am in stderr, Hello World

  也可以使用 Emacs 等在同一屏内显示两个终端。
 
  2.使用匿名管道处理标准输出。由于标准错误不会进入管道,因此不会被管道后面的程序处理。例如使用“tr”来改变标准输出的大小写。

  1. linjian@goslin:~/dev/outerr$ ./test1.sh | tr [a-z] [A-Z]
  2. INPUT:
  3. Hello World
  4. I am in stderr, Hello World
  5. I AM IN STDOUT, HELLO WORLD
  6. INPUT:

  想要更明显一点的话,可以用 ANSI 颜色特性,自己写一个简单的过滤器:

  1. linjian@goslin:~/dev/outerr$ cat color.sh
  2. #!/bin/bash
  3.  
  4. while true; do
  5.     read FOO
  6.     echo -e "\E[33;44m$FOO\E[37;40m"
  7. done

  或者直接使用像 ansi-color 这样的现成工具。

试用Windows的UNIX/POSIX子系统(SUA)

2009/05/04 | 20:07 | 分类:Windows应用 | 标签: | 1,606次阅读

  以前研究Windows的基本概念时,我就知道它有一个POSIX子系统,可以在Windows下编译运行使用了POSIX库的程序。但这一直停留在书本概念层面,直到昨天看到Jeep同学的Windows系统上安装了一个Subsystem for UNIX-based Applications时,我便决定也安装试用一下。
  有关Windows的POSIX子系统是什么、怎么用的问题,可以参考Wikipedia或Microsoft TechNet [英文][中文]上的介绍。它历经了NT时代的Microsoft POSIX subsystem、XP/2000时代的Microsoft Windows Services for UNIX (SFU)以及2003 R2/Vista/2008时代的Subsystem for UNIX-based Applications (SUA)等版本,对POSIX标准的支持日臻完善。我的系统是来自MSDNAA/IEEE的Windows Server 2008,自然要使用最新版的SUA。至于SUA和cygwin在实现机理和功能性能上有什么区别,我还没有仔细研究。但从直观感觉上,Windows原生支持的SUA是比cygwin快一点儿;按照Wikipedia上的这个说法,cygwin是对POSIX是“partial”兼容,而SFU/SUA则是“full”兼容。
  很多人安装SUA的目的并不是要向Windows移植什么重要的UNIX/Linux应用,有时候我们仅仅是为了在Windows中使用一个类UNIX的Shell以及丰富的GNU utilities,毕竟这类久经考验的命令行工具比Windows Command Prompt的那些命令好使很多。对于工作环境要求在Windows和Linux间来来回回切换的人们,也省得敲错命令。安装SUA之后,预装的Shell是C Shell和Korn Shell,还安装了包括vi、gcc在内的300多个命令行工具。同时,Windows的Path环境变量中自动添加了SUA相关目录,这样在Windows Command Prompt和Power Shell中也可以使用很多GNU utilities了。当然,UNIX Shell的内部命令是不可以在这里使用的。此外,在Power Shell中,Power Shell命令别名(如ls、cp)会优先于同名的GNU utilities调用。总之,使用SUA或cygwin这类UNIX Shell+GNU utilities的模拟环境,相比手工添加的“容错”命令或者GnuWin32这类独立命令级的移植要“真实”和顺手得多,但缺点就是体积庞大。
  为了检验SUA的能力,我拿我相对熟悉的GNU bash做了实验。从官方下载bash-4.0版源代码,在SUA的C Shell环境中解压,运行./configure通过。但在make时报错:

  1. execute_cmd.c: In function `time_command':
  2. execute_cmd.c:1145: error: storage size of `dtz' isn't known

  查看execute_cmd.c源代码,发现这句代码有注释:

  1. struct timezone dtz; /* posix doesn't define this */

  看来这个struct timezone不是POSIX标准的东西。通过上下文,我发现这里是处理time(计时)关键字的函数。于是查看configure的帮助,得知只要加一个“--disable-command-timing”参数即可禁用time关键字(这时输入time将改用/bin/time程序做计时)。再次make,execute_cmd.c通过,但又出现以下错误:

  1. getcwd.c: In function `_path_checkino':
  2. getcwd.c:80: error: `MP_RMDOT' undeclared (first use in this function)
  3. getcwd.c:80: error: (Each undeclared identifier is reported only once
  4. getcwd.c:80: error: for each function it appears in.)

  查看./lib/sh/getcwd.c的源代码,果然没有找到MP_RMDOT宏的定义。在全部源代码中搜索,发现这一定义在externs.h中,在./lib/sh/makepath.c中也有使用,唯独在./lib/sh/getcwd.c中使用了却没有引用其定义。也许这是bash-4.0的一个bug?使用Linux下的gcc编译连接是通过的,但SUA下的gcc版本也许有更严格的名字连接策略,导致无法编译通过。于是我将“#define MP_RMDOT 0x04”的定义手工加入./lib/sh/getcwd.c,再次make,全部通过。实验运行无误!
  需要注意的是,SUA提供的是编译和运行使用了POSIX库的程序的环境,并不提供UNIX二进制文件的运行支持。它编译生成和支持运行的可执行文件仍然是Windows的PE格式,而不是ELF之类。SUA支持的只是仅使用了标准库和POSIX库的程序的源代码级移植,对于使用了Linux等环境特有的系统调用的程序,也不可能不加修改地编译运行。
  有空再研究一下SUA在我们的工程中能有什么实际点的应用。

GNU bash实现机制与源代码简析

2008/05/27 | 21:36 | 分类:Linux与开源 | 标签: | 1,168次阅读

  本文是本人学习shell实现机理,分析GNU bash源代码时总结的笔记性文档。通过分析bash源代码,阐述了其主要功能模块的组织和实现方式,同时对几个特定的工作流程进行了说明。
  查看地址:
http://www.linjian.cn/files/articles/bash_study/bash_linjian.html
http://files.linjian.org/articles/bash_study/bash_linjian.html
  转载本文请注明出处。如发现文中不妥或错误之处,望不吝赐教。

为bash添加内置命令(built-in)的方法

2008/02/10 | 01:34 | 分类:Linux与开源 | 标签: | 737次阅读

  在bash命令行工作中,为常用的操作编写一个脚本或外部命令是很容易的。事实上自己添加一条内置命令(built-in)也并不难。内置命令比脚本和外部命令的执行效率更高,还可以访问脚本和外部命令无法深入的bash内部,只是可移植性下降了。
  GNU bash的源代码结构较为清晰。源代码目录(记为$(srcdir))下的builtins目录存储的是各个内置命令的源代码预定义文件(*.def)。在make的过程中,由mkbuiltins工具将它们预编译为源程序(*.c),进而编译为目标文件(*.o)。mkbuiltins工具是由同一目录下的mkbuiltins.c编译生成的,它在处理*.def文件的同时,还会生成builtins.c和builtext.h两个文件,用做bash主程序调用内置命令的接口以及各个内置命令的索引。
  要添加一条新内置命令,只需参考原有命令的存在形式即可,步骤如下:
  1、新建预定义文件:$(srcdir)/builtins/[命令名].def。可复制已有命令的预定义文件,修改其中的$PRODUCES、$BUILTIN、$FUNCTION、$SHORT_DOC等定义,使之与命令名相符。
  2、在预定义文件中建立命令处理函数,原型参考已有命令的处理函数,函数名与$FUNCTION的定义一致。参数为WORD_LIST *list,该结构的定义在$(srcdir)/command.h中。处理参数的具体方法同样可参考已有的命令(如echo)的处理函数。
  3、修改$(srcdir)/builtins/Makefile.in,参照已有的命令,分别在DEFSRC、OFILES添加对[命令名].def、[命令名].o的定义;添加[命令名].o对[命令名].def以及其它头文件的依赖关系。
  4、回到$(srcdir)下,对源代码进行configure、make,如果一切顺利的话,此时生成的bash程序将包含新添加的内置命令。
  (在GNU bash 3.2下试验通过)

 

本例中添加的命令处理函数为:

int linjian_builtin (list)
     WORD_LIST *list;
{
  printf ("This is a built-in for test by Lin Jian.\n");
  if (list)
    printf("Parameter: %s\n", list->word->word);
  return (EXECUTION_SUCCESS);
}

 

编译后试验结果如下:

#在原版bash下工作:
lj@lj-laptop:~/bash-3.2$ ps
  PID TTY          TIME CMD
 6212 pts/2    00:00:00 bash
 9893 pts/2    00:00:00 ps
lj@lj-laptop:~/bash-3.2$ linjian
-bash: linjian: command not found
#进入修改后的bash:
lj@lj-laptop:~/bash-3.2$ ./bash
lj@lj-laptop:~/bash-3.2$ ps
  PID TTY          TIME CMD
 6212 pts/2    00:00:00 bash
 9904 pts/2    00:00:00 bash
 9922 pts/2    00:00:00 ps
lj@lj-laptop:~/bash-3.2$ linjian hello!
This is a built-in for test by Lin Jian.
Parameter: hello!
lj@lj-laptop:~/bash-3.2$ type linjian
linjian is a shell builtin