我找的例子是一个统计日志的工具,webalizer. (这个工具我以前用过,似乎记得以前的版本是用perl写的,不知道现在为什么作者把它完全修改成了C,可能是为了效率,也可能根本就是我记错了。)之所以选择这个软件来作为例子,一方面是因为它是用C写的,流程比较简单,没有C++的程序那么多的枝节,而且软件功能不算复杂,代码规模不大,能够在一篇文章的篇幅里面讲完; 另外一个方面是因为恰巧前段时间我因为工作的关系把它拿来修改了一下,刚看过,还没有忘记。 :-)我采用的例子是webalizer2.01-09, 也可以到它的网站http://www.mrunix.net/webalizer/ 下载最新的版本。这是一个用C写的,处理文本文件(简单的说是这样,实际上它支持三种日志文本格式:CLF, FTP, SQUID), 并且用html的方式输出结果。读者可以自己去下载它的源代码包,并一边读文章,一边看程序。解压缩它的tar包(我download的是它的源代码tar包),在文件目录中看到这样的结果:
$ ls
aclocal.m4 dns_resolv.c lang output.h webalizer.1
CHANGES dns_resolv.h lang.h parser.c webalizer.c
configure graphs.c linklist.c parser.h webalizer.h
configure.in graphs.h linklist.h preserve.c webalizer_lang.h
COPYING hashtab.c Makefile.in preserve.h webalizer.LSM
Copyright hashtab.h Makefile.std README webalizer.png
country-codes.txt INSTALL msfree.png README.FIRST
DNS.README install-sh output.c sample.conf
首先,我阅读了它的README(这是很重要的一个环节), 大体了解了软件的功能,历史状况,修改日志,安装方法等等。然后是安装并且按照说明中的缺省方式来运行它,看看它的输出结果。(安装比较简单,因为它带了一个configure, 在没有特殊情况出现的时候,简单的./configure, make, make install就可以安装好。)然后就是阅读源代码了。我从makefile开始入手(我觉得这是了解一个软件的最好的方法)在makefile开头,有这些内容:
prefix = /usr/local
exec_prefix = ${prefix}
BINDIR = ${exec_prefix}/bin
MANDIR = ${prefix}/man/man1
ETCDIR = /etc
CC = gcc
CFLAGS = -Wall -O2
LIBS = -lgd -lpng -lz -lm
DEFS = -DETCDIR="/etc" -DHAVE_GETOPT_H=1 -DHAVE_MATH_H=1
LDFLAGS=
INSTALL= /usr/bin/install -c
INSTALL_PROGRAM=${INSTALL}
INSTALL_DATA=${INSTALL} -m 644
# where are the GD header files?
GDLIB=/usr/include
这些定义了安装的路径,执行程序的安装路径,编译器,配置文件的安装路径,编译的选项,安装程序,安装程序的选项等等。要注意的是,这些并不是软件的作者写的,而是./configure的输出结果。呵呵. :-)下面才是主题内容,也是我们关心的。
# Shouldn't have to touch below here!
all: webalizer
webalizer: webalizer.o webalizer.h hashtab.o hashtab.h
linklist.o linklist.h preserve.o preserve.h
dns_resolv.o dns_resolv.h parser.o parser.h
output.o output.h graphs.o graphs.h lang.h
webalizer_lang.h
$(CC) ${LDFLAGS} -o webalizer webalizer.o hashtab.o linklist.o preserv
e.o parser.o output.o dns_resolv.o graphs.o ${LIBS}
rm -f webazolver
ln -s webalizer webazolver
webalizer.o: webalizer.c webalizer.h parser.h output.h preserve.h
graphs.h dns_resolv.h webalizer_lang.h
$(CC) ${CFLAGS} ${DEFS} -c webalizer.c
parser.o: parser.c parser.h webalizer.h lang.h
$(CC) ${CFLAGS} ${DEFS} -c parser.c
hashtab.o: hashtab.c hashtab.h dns_resolv.h webalizer.h lang.h
$(CC) ${CFLAGS} ${DEFS} -c hashtab.c
linklist.o: linklist.c linklist.h webalizer.h lang.h
$(CC) ${CFLAGS} ${DEFS} -c linklist.c
output.o: output.c output.h webalizer.h preserve.h
hashtab.h graphs.h lang.h
$(CC) ${CFLAGS} ${DEFS} -c output.c
preserve.o: preserve.c preserve.h webalizer.h parser.h
hashtab.h graphs.h lang.h
$(CC) ${CFLAGS} ${DEFS} -c preserve.c
dns_resolv.o: dns_resolv.c dns_resolv.h lang.h webalizer.h
$(CC) ${CFLAGS} ${DEFS} -c dns_resolv.c
graphs.o: graphs.c graphs.h webalizer.h lang.h
$(CC) ${CFLAGS} ${DEFS} -I${GDLIB} -c graphs.c
好了,不用再往下看了,这些就已经足够了。从这里我们可以看到这个软件的几个源代码文件和他们的结构。webalizer.c是主程序所在的文件,其他的是一些辅助程序模块。对比一下目录里面的文件,
$ ls *.c *.h
dns_resolv.c graphs.h lang.h output.c parser.h webalizer.c
dns_resolv.h hashtab.c linklist.c output.h preserve.c webalizer.h
graphs.c hashtab.h linklist.h parser.c preserve.h webalizer_lang.h