Clamav杀毒软件源码分析笔记[二]
刺猬@http://blog.csdn.net/littlehedgehog
[读取命令行参数]
开始正儿八经地分析源码了. 从哪里入手呢,当然还是按照书上的来,我们先来看看Clamd的程序如何从生到死,Clamd是杀毒软件的服务端,我们在客户端提交要查杀的信息,然后Clamd服务端进行按照我们指令办事,然后把结果返回给我们。这个貌似是unix程序的一个经典构架了,所谓的客户端直接跟用户打交道,然后解析用户输入,把真正要做的工作再传给服务端完成。服务端其实是一个无名英雄了。 话说回来这里第一步我们就要处理命令行下琐碎的各个参数选项.为了让Clamd服务端按照我们要求来运行,这貌似是个相当麻烦的工作,比女人都麻烦,呵呵. 不过还好 上有不少linux fans 都写过相应的博文了. 还是先来看源码吧: [./clamd/options.c]
- /* 这个是clamd的主函数 文件名为options,那么这个主函数就主要在处理这个选项(参数选项)问题了*/int main(int argc, char **argv){ int ret, opt_index, i, len; struct optstruct *opt; const char *getopt_parameters = “hc:V”; static struct option long_options[] = { {“help”, 0, 0, ‘h’}, {“config-file”, 1, 0, ‘c’}, {“version”, 0, 0, ‘V’}, {“debug”, 0, 0, 0}, {0, 0, 0, 0} };#if defined(C_LINUX) && defined(CL_DEBUG) /* njh@bandsman.co.uk: create a dump if needed */ struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_CORE, &rlim) < 0) perror(“setrlimit”);#endif opt=(struct optstruct*)mcalloc(1, sizeof(struct optstruct)); opt->optlist = NULL; opt->filename = NULL; while (1) { opt_index=0; ret=getopt_long(argc, argv, getopt_parameters, long_options, &opt_index); if (ret == -1) break; switch (ret) { case 0: register_long_option(opt, long_options[opt_index].name); break; default: if (strchr(getopt_parameters, ret)) register_char_option(opt, ret); else { fprintf(stderr, “ERROR: Unknown option passed./n”); free_opt(opt); exit(40); } } } if (optind < argc) { len=0; /* count length of non-optin arguments 下面是处理非选项参数*/ for (i=optind; i<argc; i++) len+=strlen(argv[i]); len=len+argc-optind-1; /* add spaces between arguments */ opt->filename=(char*)mcalloc(len + 256, sizeof(char)); for (i=optind; i<argc; i++) { strncat(opt->filename, argv[i], strlen(argv[i])); if (i != argc-1) strncat(opt->filename, ” “, 1); } } clamd(opt); free_opt(opt); cli_dbgmsg(“exit main()”); return(0);}
代码不长,逻辑也不复杂. 不过在理解GNU提供给我们截获参数选项操作之前,我们确实应该先来明确一下处理的关键点在哪里.比如说有如下命令行 command -h 这是我们来获取command使用帮助的时候键入的命令. 也就是说按照约定,我们键入-h 就是表明要显示出帮助手册,但是在linux/unix里,我们有时候也键入全称来输入参数,比如说command –help 两横加上全称,同样也是表明我们需要帮助手册.为了能处理以上两种情况,我们需要做更多的准备. 好了,下面的文字是 上拷来的,呵呵,这也是代码复用的思想…
============================================
摘自冰火天地 《getopt_long及其使用》
========================================
Linux系统下,需要大量的命令行选项,如果自己手动解析他们的话实在是有违软件复用的思想,不过还好,GNU C library留给我们一个解析命令行的接口(X/Open规范),好好使用它可以使你的程序改观不少。
使用getopt_long()需要引入头文件
#include <getopt.h>
现在我们使用一个例子来说明它的使用。
一个应用程序需要如下的短选项和长选项。
短选项 长选项 作用
-h –help 输出程序命令行参数说明然后退出
-o filename –output filename 给定输出文件名
-v –version 显示程序当前版本后退后
为了使用getopt_long函数,我们需要先确定两个结构:
1.一个字符串,包括所需要的短选项字符,如果选项后有参数,字符后加一个”:”符 。本例中,这个字符串应该为”ho:v”。(因为-o后面有参数filename,所以字符后面要加”:”)
2.一个包含长选项字符串的结构体数组,每一个结构体包含4个域,第一个域为长选项字符串,第二个域是一个标识,只能为0或1,分别代表没有、有。第三个域永远为NULL。第四个域为对应的短选项字符串。结构体数组的最后一个元素全部为NULL和0,标识结束。在本例中,它应该像一下的样子:
const struct option long_options[] = {
{ “help”, 0, NULL, ‘h’ },
{ “output”, 1, NULL, ‘o’ },
{ “version”, 0, NULL, ‘v’ },
{ NULL, 0, NULL, 0}
};
调用时需要把main的两个参数argc和argv以及上述两个数据结构传给getopt_long。
每次调用getopt_long,它会解析一个符 ,返回相应的短选项字符,如果解析完毕返回-1。所以需要使用一个循环来处理所有的参数,而相应的循环里会使用switch语句进行选择。如果getopt_long遇到一个无效的选项字符,它会打印一个错误消息并且返回’,很多程序会打印出帮助信息并且中止运行;当getopt_long解析到一个长选项并且发现后面没有参数则返回’:’,表示缺乏参数。当处理一个参数时,全局变量optarg指向下一个要处理的变量。当getopt_long处理完所有的选项后,全局变量optind指向第一个未知的选项索引。
这一个例子代码为下:
- //编译使用gcc -o getopt_long getopt_long.c#include <getopt.h>#include <stdio.h>#include <stdlib.h>/*程序的名字*/const char* program_name;/* 打印程序参数 */void print_usage (FILE* stream, int exit_code){fprintf (stream, “Usage: %s options [ inputfile … ]/n”, program_name);fprintf (stream, ” -h –help 显示这个帮助信息./n” ” -o –output filename 将输出定位到文件./n” ” -v –version 打印版本信息./n”);exit (exit_code);}/* 主程序 */int main (int argc, char* argv[]){int next_option;//下一个要处理的参数符 int haveargv = 0;//是否有我们要的正确参数,一个标识 /* 包含短选项字符的字符串,注意这里的‘:’ */ const char* const short_options = “ho:v”; /* 标识长选项和对应的短选项的数组 */ const struct option long_options[] = { { “help”, 0, NULL, ‘h’ }, { “output”, 1, NULL, ‘o’ }, { “version”, 0, NULL, ‘v’ }, { NULL, 0, NULL, 0 }};//最后一个元素标识为NULL /* 此参数用于承放指定的参数,默认为空 */const char* output_filename = NULL;/* 一个标志,是否显示版本 */int verbose = 0;/* argv[0]始终指向可执行的文件文件名 */ program_name = argv[0]; do{ next_option = getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case ‘h’: /* -h or –help */ haveargv = 1; print_usage (stdout, 0); case ‘o’: /* -o or –output */ /* 此时optarg指向–output后的filename */ output_filename = optarg; haveargv = 1; break; case ‘v’: /* -v or –version */ verbose = 1; haveargv = 1; break; case ‘:’: /* 缺乏长选项内容 */ break; case ‘: /* 出现一个未指定的参数*/ print_usage (stderr, 1); case -1: /* 处理完毕后返回-1 */ if (!haveargv) { print_usage (stderr, 1); } break; default: /* 未指定的参数出现,出错处理 */ print_usage (stderr, 1); break; }}while (next_option !=-1); if (verbose){ int i; for (i = optind; i < argc; ++i) printf (“Argument: %s/n”, argv[i]);} return 0;}
============================================
文章节录完
============================================
- /*短选项加入链表 比如–config_file=test 注意 这里虽然是个长选项 但是它含有短字符代表,即是字符c {“config-file”, 1, 0, ‘c’} */void register_char_option(struct optstruct *opt, char ch){ struct optnode *newnode; newnode = (struct optnode *) mmalloc(sizeof(struct optnode)); newnode->optchar = ch; if (optarg != NULL) { newnode->optarg = (char *) mcalloc(strlen(optarg) + 1, sizeof(char)); //参数值,这里就是test strcpy(newnode->optarg, optarg); } else newnode->optarg = NULL; newnode->optname = NULL; newnode->next = opt->optlist; //连入链表 opt->optlist = newnode;}
opt和newnode的结构如下所示:
- struct optnode{ char optchar; //短选项名 比如文中举例的 h 选项 char *optarg; //参数值, 有时候我们对参数要设定值的,比如 -p 23 这里23就是参数值 char *optname; //长选项名, 文中举例的help这个字符串就应该放在这里,跟前面h对应 struct optnode *next; //下一个节点呗,我们要连成链表阿};struct opt
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!