目录
编辑
1、各种输出格式
2、处理递归调用
前面所描述的输出格式被称为 GNU 类型。除此之外,cflow 也可以使用 POSIX 产生格式化的输出。这种格式,输出的每一行都以一个参考数字开始,比如,最开始是输出行的顺序号,后面跟随每一个嵌套层的固定长度的缩进。然后如果有的话依次是函数的名字、冒号、函数的原型。紧跟在函数原型后面的是定义的位置(包括文件名和行号)。函数顶一个位置都被尖括号围住。如果函数的定义没有找到,该行将会以一个空的尖括号结尾。
使用格式化输出要么在命令行中通过--format=posix (-f posix)选项指定,要么设置环境变量 POSIXLY_CORRECT 。
使用 POSIX 格式处理我们的样例文件,如下:
1 main: int (int argc,char **argv),
2 fprintf: <>
3 who_am_i: int (void),
4 getpwuid: <>
5 geteuid: <>
6 getenv: <>
7 fprintf: <>
8 printf: <>
是否在输出中要包含函数的参数列表现在并不清楚。默认情况下 cflow 将会全部打印它们。然而一些程序使用 cflow 分析时希望省略参数列表,这可以使用--omit-arguments 选项实现。
cflow 未来的版本中将会提供更多的输出格式,包括 XML 和 HTML 输出。目前你可以使用 VCG 工具来创建典型的图。根据 xvcg 来转变输出格式可以使用cflow2vcg 程序在 GPL 下都是可用的。
Cflow2vcg 期望使用 POSIX 格式图,每层嵌套的缩进为一个水平制表符,第 0 层使用额外的 tab 字符,在函数声明中没有参数列表。这样用可兼容 cflow2vcg 产生的输出格式,调用 cflow 如下:
cflow --format=posix --omit-arguments \
--level-indent='0=\t' --level-indent='1=\t' \
--level-indent=start='\t'
你可以使用下面的脚本来虚拟调用这三个工具:
#! /bin/sh
cflow --format=posix --omit-arguments \
--level-indent='0=\t' --level-indent='1=\t' \
--level-indent=start='\t' $* |
cflow2vcg | xvcg -
有时候程序中包含调用自己的函数。GNU 输出格式为这种函数提供了特殊的表示。在结束字符-冒号之前,会有一个标号‘(R)’作为递归函数的标志。随后递归调用处会在行结尾使用‘(recursive: see refline)’标识出。这里的 refline 表示递归函数的跟定义所处的位置。
为了说明这个,我们考虑下面的程序,他打印递归目录列表,允许在任意嵌套级截断:
#include
#include
#include
#include
#include
#include
#include
/* Return true if file NAME is a directory. */
static int
isdir (char *name)
{
struct stat st;
if (stat (name, &st))
{
perror (name);
return 0;
}
return S_ISDIR (st.st_mode);
}
static char *ignored_names[] = { ".", "..", NULL };
/* Return true if NAME should not be recursed into */
int
ignorent (char *name)
{
char **p;
for (p = ignored_names; *p; p++)
if (strcmp (name, *p) == 0)
return 1;
return 0;
}
int max_level = -1;
/* Print contents of the directory PREFIX/NAME.
Prefix each output line with LEVEL spaces. */
void
printdir (int level, char *name)
{
DIR *dir;
struct dirent *ent;
char cwd[512];
if (!getcwd(cwd, sizeof cwd))
{
perror ("cannot save cwd\n");
_exit (1);
}
chdir (name);
dir = opendir (".");
if (!dir)
{
perror (name);
_exit (1);
}
while ((ent = readdir (dir)))
{
printf ("%*.*s%s", level, level, "", ent->d_name);
if (ignorent (ent->d_name))
printf ("\n");
else if (isdir (ent->d_name))
{
printf ("/");
if (level + 1 == max_level)
putchar ('\n');
else
{
printf (" contains:\n");
printdir (level + 1, ent->d_name);
}
}
else
printf ("\n");
}
closedir (dir);
chdir (cwd);
}
int
main (int argc, char **argv)
{
if (argc < 2)
{
fprintf (stderr, "usage: d [-MAX] DIR [DIR...]\n");
return 1;
}
if (argv[1][0] == '-')
{
if (!(argv[1][1] == '-' && argv[1][2] == 0))
max_level = atoi (&argv[1][1]);
--argc;
++argv;
}
while (--argc)
printdir (0, *++argv);
return 1;
}
运行 cflow 处理这个程序生成如下的图:
$ cflow --number d.c
1 main() :
2 fprintf()
3 atoi()
4 printdir() (R):
5 getcwd()
6 perror()
7 chdir()
8 opendir()
9 readdir()
10 printf()
11 ignorent() :
12 strcmp()
13 isdir() :
14 stat()
15 perror()
16 S_ISDIR()
17 putchar()
18 printdir()
(recursive: see 4)
19 closedir()
第 4 行的描述显示 printdir 是递归函数。递归调用是在第 18 行。
带你走进Cflow (一)-CSDN博客