会员注册
会员登陆
取回密码
欢迎您回来
实易文章 || 发表文章 || 管理文章

如何阅读源代码

分类:: Unix/Linux / 发表时间 :: 2005-07-13 19:13:52
作者 :: 16hot | 人气 ::  |  评论数目 (0) | 发送 | 来源 ::


By Wing


  写在前面的话:
  自从我在linuxaid.com.cn上发表一些文章开始,就不断的有网友发来电子邮件,或者是就其中某些问题进行探讨,或者是查询其他文章的地址(往往这些网友看的是其他网站转载的我的文章),我很高兴自己写出的文章有这么多人回应,因为这是对我最好的赞赏,也很高兴有这么多人对我的文章感兴趣。但是常常因为工作关系。有很多邮件是询问我的其他文章在哪里能够找到,我不一定能够及时回复,也觉得回复同样的问题比较麻烦,所以在这里重复一下,我为linuxaid.com.cn写的文章都能在www.linuxaid.com.cn的应用开发栏目中找到,我的一部分文章收集在bambi.may10.ca/~ariesram/articles下面(这是一个很简陋的网页,只有文本格式的文章,也欢迎有兴趣的网友帮我设计一下网页),我的邮件地址:ariesram@linuxaid.com.cn, 或者ariesram@may10.ca。请转载文章的网站保留这一说明,欢迎网友写email给我探讨问题,虽然我不能保证能及时回复。
  正文:
  由于工作的关系,我常常需要读一些源代码,并在上面做一些修改并且拿来使用,或者是借鉴其中的某些部分。可以说,open source对于程序员来说,是很有意义的事情。根据我的经验,读源代码,至少有3个好处。第一个好处是可以学习到很多编程的方法,看好的源代码,对于提高自己的编程水平,比自己写源代码的帮助更大。当然不是说不用自己写,而是说,自己写代码的同时,可以从别人写的好的源代码中间学习到更多的编程方法和技巧。第二个好处是,可以提高自己把握大规模源代码的能力。一个比较大型的程序,往往都是经过了很多个版本很长的时间,有很多人参与开发,修正错误,添加功能而发展起来的。所以往往源代码的规模都比较大,少则10-100多k, 多的有好几十个MB. 在阅读大量源代码的时候,能够提高自己对大的软件的把握能力,快速了解脉络,熟悉细节,不仅仅是编程技巧,还能在程序的架构,设计方面提高自己的能力。(这里说一句题外话,这本书相信很多人都看过,而且很多人对它推崇备至,奉为经典。现在也出了不少书,都是冠以"设计模式"这一名称。在书中就提到,设计模式并不是一本教材,不是教你如何去编程序,而是把平时编程中一些固定的模式记录下来,加以不断的测试和改进,分发给广大程序员的一些经验之谈。我在看这本书的时候,有一些地方一些设计方法往往让我有似曾相识的感觉,另外一些则是我以前就常常用到的。而这些经验的获得,一部分得益于自己的编码过程,另外一个很重要的来源就是阅读别人写的源代码。)阅读源代码第三个好处,就是获得一些好的思想。比如,有很多人在开始一个软件项目之前都喜欢到sourceforge.net上去找一下,是否有人以前做过相同或者相似的软件,如果有,则拿下来读一读,可以使自己对这个软件项目有更多更深的认识。我以前曾经想找一本关于如何阅读源代码的书来看看,却没有找到。相反,倒是找到了不少分析源代码的书,比如Linux kernel, Apache source, 等等。所以我想,为什么不自己来写下一些经验和大家交流呢?(当然不是写书,没有那个能力也没有那个时间。)所以在这里我准备用一个例子来写一下如何阅读源代码,分享一些经验,算是抛砖引玉吧!
  我找的例子是一个统计日志的工具,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

  于是,让我们从webalizer.c开始吧。
  作为一个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中。
  在这个h文件中,当然会有一些数据结构的定义,比如:

struct nlist { char string[80];
/* list struct for HIDE items */
              struct nlist *next; };
typedef struct nlist *NLISTPTR;
struct glist { char string[80];
/* list struct for GROUP items */
                char name[80];
              struct glist *next; };
typedef struct glist *GLISTPTR;

  这是两个链表结构。还有

extern GLISTPTR group_sites ; /* "group" lists */
extern GLISTPTR group_urls ;
extern GLISTPTR group_refs ;

  这些都是链表, 太多了,不用一一看得很仔细,因为目前也看不出来什么东西。当然要注意它们是extern的, 也就是说,可以在其他地方(文件)看到它们的数值(类似于C++中的public变量)。这里还定义了4个函数:

extern char *isinlist(NLISTPTR, char *);
/* scan list for str */
extern char *isinglist(GLISTPTR, char *);
/* scan glist for str */
extern int add_nlist(char *, NLISTPTR *);
/* add list item */
extern int add_glist(char *, GLISTPTR *);
/* add group list item */

  注意,这些都是extern的,也就是说,可以在其他地方见到它们的调用(有点相当于C++中的public函数)。再来看看linklist.c,

NLISTPTR new_nlist(char *); /* new list node */
void del_nlist(NLISTPTR *); /* del list */

GLISTPTR new_glist(char *, char *); /* new group list node */
void del_glist(GLISTPTR *); /* del group list */
int isinstr(char *, char *);

  这5个函数是内部使用的(相当于C++中的private), 也就是说,这些函数只被isinlist(NLISTPTR, char *), isinglist(GLISTPTR, char *), add_nlist(char *, NLISTPTR *), add_glist(char *, GLISTPTR *)调用,而不会出现在其他地方。所以,我们先来看这几个内部函数。举例来说,

add_nlist(char *)
NLISTPTR new_nlist(char *str)
{
  NLISTPTR newptr;
  if (sizeof(newptr->string) < strlen(str))
  {
      if (verbose)
    fprintf(stderr,"[new_nlist] %s ",msg_big_one);
  }
  if (( newptr = malloc(sizeof(struct nlist))) != NULL)
    {strncpy(newptr->string, str, sizeof(newptr->string));
    newptr->next=NULL;}
  return newptr;
}

  这个函数分配了一个struct nlist, 并且把其中的string赋值为str, next赋值为NULL.这实际上是创建了链表中的一个节点。verbose是一个全局变量,定义了输出信息的类型,如果verbose为1,则输出很详细的信息,否则输出简略信息。这是为了调试或者使用者详细了解程序情况来用的。不是重要内容,虽然我们常常可以在这个源程序的其他地方看到它。另外一个函数:

void del_nlist(NLISTPTR *list)
{
  NLISTPTR cptr,nptr;
  cptr=*list;
  while (cptr!=NULL)
  {
      nptr=cptr->next;
      free(cptr);
      cptr=nptr;
  }
}

  这个函数删除了一个nlist(也可能是list所指向的那一个部分开始知道链表结尾),比较简单。看完了这两个内部函数,可以来看

/*********************************************/
/* ADD_NLIST - add item to FIFO linked list */
/*********************************************/
int add_nlist(char *str, NLISTPTR *list)
{
  NLISTPTR newptr,cptr,pptr;
  if ( (newptr = new_nlist(str)) != NULL)
  {
      if (*list==NULL) *list=newptr;
      else
      {
        cptr=pptr=*list;
        while(cptr!=NULL) { pptr=cptr; cptr=cptr->next; };
        pptr->next = newptr;
      }
  }
  return newptr==NULL;
}

  这个函数是建立了一个新的节点,把参数str赋值给新节点的string, 并把它连接到list所指向链表的结尾。另外的三个函数:new_glist(), del_glist(), add_glist()完成的功能和上述三个差不多,所不同的只是它们所处理的数据结构不同。看完了这几个函数,我们回到main程序。接下来是,

  /* setup our internal variables */
  init_counters(); /* initalize main counters */

  我们所阅读的这个软件是用来分析日志并且做出统计的,那么这个函数的名字已经告诉了我们,这是一个初始化计数器的函数。简略的看看吧!

$ grep init_counters *.h
webalizer.h:extern void init_counters();
在webalizer.c中找到:
void init_counters()
{
  int i;
  for (i=0;i1) printf("%s ",msg_no_hist);
}
/*********************************************/
/* PUT_HISTORY - write out history file */
/*********************************************/
void put_history()
{
  int i;
  FILE *hist_fp;

  hist_fp = fopen(hist_fname,"w");

  if (hist_fp)
  {
      if (verbose>1) printf("%s ",msg_put_hist);
      for (i=0;i0) b--; break;
      case '(': if (q) break; p++; break;
      case ')': if (q) break; if (p>0) p--; break;
      }
      cp++;
  }
}

  从parser.h头文件中就可以看到,这个函数是一个内部函数,这个函数把一行字符串中间的空格字符用''字符(结束字符)来代替,同时考虑了不替换在双引号,方括号,圆括号中间的空格字符以免得将一行数据错误的分隔开了。(请参考WEB日志的文件格式,可以更清楚的理解这一函数)

int parse_record_web(char *buffer)
{
  int size;
  char *cp1, *cp2, *cpx, *eob, *eos;
  size = strlen(buffer); /* get length of buffer */
  eob = buffer+size; /* calculate end of buffer */
  fmt_logrec(buffer); /* seperate fields with 's */
  /* HOSTNAME */
  cp1 = cpx = buffer; cp2=log_rec.hostname;
  eos = (cp1+MAXHOST)-1;
  if (eos >= eob) eos=eob-1;
  while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;
  *cp2 = '';
  if (*cp1 != '')
  {
      if (verbose)
      {
        fprintf(stderr,"%s",msg_big_host);
        if (debug_mode) fprintf(stderr,": %s ",cpx);
        else fprintf(stderr," ");
      }
      while (*cp1 != '') cp1++;
  }
  if (cp1 < eob) cp1++;
  /* skip next field (ident) */
  while ( (*cp1 != '') && (cp1 < eob) ) cp1++;
  if (cp1 < eob) cp1++;
  /* IDENT (authuser) field */
  cpx = cp1;
  cp2 = log_rec.ident;
  eos = (cp1+MAXIDENT-1);
  if (eos >= eob) eos=eob-1;
  while ( (*cp1 != '[') && (cp1 < eos) ) /* remove embeded spaces */
  {
      if (*cp1=='') *cp1=' ';
      *cp2++=*cp1++;
  }
  *cp2--='';
  if (cp1 >= eob) return 0;
  /* check if oversized username */
  if (*cp1 != '[')
  {
      if (verbose)
      {
        fprintf(stderr,"%s",msg_big_user);
        if (debug_mode) fprintf(stderr,": %s ",cpx);
        else fprintf(stderr," ");
      }
      while ( (*cp1 != '[') && (cp1 < eob) ) cp1++;
  }
  /* strip trailing space(s) */
  while (*cp2==' ') *cp2--='';
  /* date/time string */
  cpx = cp1;
  cp2 = log_rec.datetime;
  eos = (cp1+28);
  if (eos >= eob) eos=eob-1;
  while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;
  *cp2 = '';
  if (*cp1 != '')
  {
      if (verbose)
      {
        fprintf(stderr,"%s",msg_big_date);
        if (debug_mode) fprintf(stderr,": %s ",cpx);
        else fprintf(stderr," ");
      }
      while (*cp1 != '') cp1++;
  }
  if (cp1 < eob) cp1++;
  /* minimal sanity check on timestamp */
  if ( (log_rec.datetime[0] != '[') ||
        (log_rec.datetime[3] != '/') ||
        (cp1 >= eob)) return 0;
  /* HTTP request */
  cpx = cp1;
  cp2 = log_rec.url;
  eos = (cp1+MAXURL-1);
  if (eos >= eob) eos = eob-1;
  while ( (*cp1 != '') && (cp1 != eos) ) *cp2++ = *cp1++;
  *cp2 = '';
  if (*cp1 != '')
  {
      if (verbose)
      {
        fprintf(stderr,"%s",msg_big_req);
        if (debug_mode) fprintf(stderr,": %s ",cpx);
        else fprintf(stderr," ");
      }
      while (*cp1 != '') cp1++;
  }
  if (cp1 < eob) cp1++;
  if ( (log_rec.url[0] != '"') ||
        (cp1 >= eob) ) return 0;
  /* response code */
  log_rec.resp_code = atoi(cp1);
  /* xfer size */
  while ( (*cp1 != '') && (cp1 < eob) ) cp1++;
  if (cp1 < eob) cp1++;
  if (*cp1'9') log_rec.xfer_size=0;
  else log_rec.xfer_size = strtoul(cp1,NULL,10);
  /* done with CLF record */
  if (cp1>=eob) return 1;
  while ( (*cp1 != '') && (*cp1 != ' ') && (cp1 < eob) )
  cp1++;
  if (cp1 < eob) cp1++;
  /* get referrer if present */
  cpx = cp1;
  cp2 = log_rec.refer;
  eos = (cp1+MAXREF-1);
  if (eos >= eob) eos = eob-1;
  while ( (*cp1 != '') && (*cp1 != ' ') && (cp1 != eos) )
  *cp2++ = *cp1++;
  *cp2 = '';
  if (*cp1 != '')
  {
      if (verbose)
      {
        fprintf(stderr,"%s",msg_big_ref);
        if (debug_mode) fprintf(stderr,": %s ",cpx);
        else fprintf(stderr," ");
      }
      while (*cp1 != '') cp1++;
  }
  if (cp1 < eob) cp1++;
  cpx = cp1;
  cp2 = log_rec.agent;
  eos = cp1+(MAXAGENT-1);
  if (eos >= eob) eos = eob-1;
  while ( (*cp1 != '') && (cp1 != eos) )
  *cp2++ = *cp1++;
  *cp2 = '';
  return 1; /* maybe a valid record, return with TRUE */
}

  该函数,一次读入一行(其实是一段日志数据中间的一个域,因为该行数据已经被fmt_logrec分开成多行数据了。根据CLF中的定义,检查该数据并将其拷贝到log_rec结构中去,如果检查该数据有效,则返回1。回到主程序,

    /* convert month name to lowercase */
        for (i=4;i59)||(rec_sec>59)||(rec_year mh_hit) mh_hit = ht_hit;
      if (total_rec > (total_ignore+total_bad))
      /* did we process any? */
      {
        if (incremental)
        {
            if (save_state()) /* incremental stuff */
            {
              /* Error: Unable to save current run data */
              if (verbose) fprintf(stderr,"%s ",msg_data_err);
              unlink(state_fname);
            }
        }
        month_update_exit(rec_tstamp); /* calculate exit pages */
        write_month_html(); /* write monthly HTML file */
        write_main_index(); /* write main HTML file */
        put_history(); /* write history */
      }
      end_time = times(&mytms);
      /* display timing totals? */
      if (time_me' '(verbose>1))
      {
        printf("%lu %s ",total_rec, msg_records);
        if (total_ignore)
        {
            printf("(%lu %s",total_ignore,msg_ignored);
            if (total_bad) printf(", %lu %s) ",total_bad,msg_bad);
              else printf(") ");
        }
        else if (total_bad) printf("(%lu %s) ",total_bad,msg_bad);
        /* get processing time (end-start) */
        temp_time = (float)(end_time-start_time)/CLK_TCK;
        printf("%s %.2f %s", msg_in, temp_time, msg_seconds);
        /* calculate records per second */
        if (temp_time)
          i=( (int)( (float)total_rec/temp_time ) );
        else i=0;
        if ( (i>0) && (i

[ 返回 ]


■ 相关文章
· 让firefox自动调用下载器 (2005-09-07)
· 浅谈linux优化及安全配置 (2005-09-07)
· python入门1 (2005-09-07)
· 以非超级用户身份安装 mod_perl (2005-09-07)
· FC3中的JAVA安装及配置 (2005-09-07)
· Perl与MandrakeLinux (2005-09-07)
· Apache服务器实现用户验证 (2005-09-07)
· 受限制环境安装Perl模块方法 (2005-09-07)
· Linux内核研究系列之可执行文件格式 (2005-07-13)
· 如何阅读源代码--工具篇 (2005-07-13)
· 如何阅读源代码 (2005-07-13)
· Linux内核编程风格 (2005-07-13)
· Linux网络代码导读v0.2 (2005-07-13)
· Linuxinodecache分析 (2005-07-13)
· 目录项缓存dcache (2005-07-13)
· Linux对I/O端口资源的管理 (2005-07-13)
· Linux对ISA总线DMA的实现 (2005-07-13)
· 基于i386的Linux实现特点剖析——基础的基础 (2005-07-13)
· 基于i386的Linux实现特点剖析——关于中断 (2005-07-13)
· 基于i386体系结构的Linux实现特点剖析——内存与进程 (2005-07-13)
· ELF可执行联接规范(英汉对照版) (2005-07-13)
· Linux内核0.11(0.95)详细注释 (2005-07-13)
· ar和nm命令的使用 (2005-07-13)
· JIDEv1.7 (2005-07-13)
· Rhide-1.4.7 (2005-07-13)
· KDevelop1.3 (2005-07-13)
· Xwpe1.5.26a (2005-07-13)
· XwpeFAQ (2005-07-13)
· C-Forge1.6-4 (2005-07-13)
· cvs客户端大全 (2005-07-13)

■ 发表评论
友情提示: 本站不允许匿名发表评论。如果您是会员,请先登陆;否则,请先注册
如下内容仅代表作者个人观点,本站概不负责!
评论标题:
评论内容:
  

■ 相关评论 更多评论...
http://www.isyi.com
Copyright © 2002-2005 实易数码. All rights Reserved 
版权声明:实易数码是本Blog托管服务提供商。实易数码不承担任何责任,请与Blog使用者联系解决。
粤ICP备05023051号