作为一个C程序,在头文件里面,和C文件里面定义的extern变量,结构等等肯定不会少,但是,单独看这些东西我们不可能对这个程序有什么认识。所以,从main函数入手,逐步分析,在需要的时候再回头来看这些数据结构定义才是好的方法。(顺便说一句,Visual C++, 等windows下的IDE工具提供了很方便的方法来获取函数列表,C++的类列表以及资源文件,对于阅读源代码很有帮助。Unix/Linux也有这些工具,但是,我们在这里暂时不说,而只是通过最简单的文本编辑器vi来讲)。跳过webalizer.c开头的版权说明部分(GPL的),和数据结构定义,全局变量声明部分,直接进入main()函数。在函数开头,我们看到:
/* initalize epoch */
epoch=jdate(1,1,1970); /* used for timestamp adj. */
/* add default index. alias */
add_nlist("index.",&index_alias);
这两个函数暂时不用仔细看,后面会提到,略过。
sprintf(tmp_buf,"%s/webalizer.conf",ETCDIR);
/* check for default config file */
if (!access("webalizer.conf",F_OK))
get_config("webalizer.conf");
else if (!access(tmp_buf,F_OK))
get_config(tmp_buf);
从注释和程序本身可以看出,这是查找是否存在一个叫做webalizer.conf的配置文件,如果当前目录下有,则用get_config来读入其中内容,如果没有,则查找ETCDIR/webalizer.conf是否存在。如果都没有,则进入下一部分。(注意:ETCDIR = @ETCDIR@在makefile中有定义)
/* get command line options */
opterr = 0; /* disable parser errors */
while ((i=getopt(argc,argv,"a:A:c:C:dD:e:E:fF:
g:GhHiI:l:Lm:M:n:N:o:pP:qQr:R:s:S:t:Tu:U:vVx:XY"))!=EOF)
{
switch (i)
{
case 'a': add_nlist(optarg,&hidden_agents); break;
/* Hide agents */
case 'A': ntop_agents=atoi(optarg); break;
/* Top agents */
case 'c': get_config(optarg); break;
/* Config file */
case 'C': ntop_ctrys=atoi(optarg); break;
/* Top countries */
case 'd': debug_mode=1; break;
/* Debug */
case 'D': dns_cache=optarg; break;
/* DNS Cache filename */
case 'e': ntop_entry=atoi(optarg); break;
/* Top entry pages */
case 'E': ntop_exit=atoi(optarg); break;
/* Top exit pages */
case 'f': fold_seq_err=1; break;
/* Fold sequence errs */
case 'F': log_type=(optarg[0]=='f')?
LOG_FTP:(optarg[0]=='s')?
LOG_SQUID:LOG_CLF; break;
/* define log type */
case 'g': group_domains=atoi(optarg); break;
/* GroupDomains (0=no) */
case 'G': hourly_graph=0; break;
/* no hourly graph */
case 'h': print_opts(argv[0]); break;
/* help */
case 'H': hourly_stats=0; break;
/* no hourly stats */
case 'i': ignore_hist=1; break;
/* Ignore history */
case 'I': add_nlist(optarg,&index_alias); break;
/* Index alias */
case 'l': graph_lines=atoi(optarg); break;
/* Graph Lines */
case 'L': graph_legend=0; break;
/* Graph Legends */
case 'm': visit_timeout=atoi(optarg); break;
/* Visit Timeout */
case 'M': mangle_agent=atoi(optarg); break;
/* mangle user agents */
case 'n': hname=optarg; break;
/* Hostname */
case 'N': dns_children=atoi(optarg); break;
/* # of DNS children */
case 'o': out_dir=optarg; break;
/* Output directory */
case 'p': incremental=1; break;
/* Incremental run */
case 'P': add_nlist(optarg,&page_type); break;
/* page view types */
case 'q': verbose=1; break;
/* Quiet (verbose=1) */
case 'Q': verbose=0; break;
/* Really Quiet */
case 'r': add_nlist(optarg,&hidden_refs); break;
/* Hide referrer */
case 'R': ntop_refs=atoi(optarg); break;
/* Top referrers */
case 's': add_nlist(optarg,&hidden_sites); break;
/* Hide site */
case 'S': ntop_sites=atoi(optarg); break;
/* Top sites */
case 't': msg_title=optarg; break;
/* Report title */
case 'T': time_me=1; break; /* TimeMe */
case 'u': add_nlist(optarg,&hidden_urls); break;
/* hide URL */
case 'U': ntop_urls=atoi(optarg); break;
/* Top urls */
case 'v':
case 'V': print_version(); break;
/* Version */
case 'x': html_ext=optarg; break;
/* HTML file extension */
case 'X': hide_sites=1; break;
/* Hide ind. sites */
case 'Y': ctry_graph=0; break;
/* Supress ctry graph */
}
}
if (argc - optind != 0) log_fname = argv[optind];
if ( log_fname && (log_fname[0]=='-')) log_fname=NULL;
/* force STDIN? */
/* check for gzipped file - .gz */
if (log_fname) if (!strcmp((log_fname+strlen(log_fname)-3),".gz"))
gz_log=1;
这一段是分析命令行参数及开关。(getopt()的用法我在另外一篇文章中讲过,这里就不再重复了。)可以看到,这个软件虽然功能不太复杂,但是开关选项还是不少。大多数的unix/linux程序的开头部分都是这个套路,初始化配置文件,并且读入分析命令行。在这段程序中,我们需要注意一个函数:add_nlist(). print_opts(), get_config()等等一看就明白,就不用多讲了。这里我们已经是第二次遇到add_nlist这个函数了,就仔细看看吧。
$ grep add_nlist *.h
linklist.h:extern int add_nlist(char *, NLISTPTR *);
/* add list item */
可以发现它定义在linklist.h中。