假定让您负责维护程序 prog。有时在程序启动时,出现错误消息 "out of storage"。现在您要使用 cscope 查找生成该消息的代码部分。您可以按以下方式执行。
cscope 是一个面向屏幕的工具,该工具仅可用于终端信息实用程序 (terminfo) 数据库中列出的终端。确保已将 TERM 环境变量设置为您的终端类型,以便 cscope 可以验证它是否列在 terminfo 数据库中。如果您尚未这样做,请为 TERM 赋值并将其输出到 shell,如下所示:
在 Bourne shell 中,键入:
$ TERM=term_name; export TERM
在 C shell 中,键入:
% setenv TERM term_name
现在,您可能希望为 EDITOR 环境变量赋值。缺省情况下,cscope 会调用 vi 编辑器。(本章中的示例说明了 vi 的用法。)如果您不想使用 vi,请将 EDITOR 环境变量设置为您选择的编辑器并导出 EDITOR,如下所示:
在 Bourne shell 中,键入:
$ EDITOR=emacs; export EDITOR
在 C shell 中,键入:
% setenv EDITOR emacs
您可能必须编写 cscope 与您的编辑器之间的接口。有关详细信息,请参见编辑器的命令行语法。
如果仅希望使用 cscope 进行浏览(而不进行编辑),则可以将 VIEWER 环境变量设置为 pg 并导出 VIEWER。然后 cscope 将调用 pg 而不是 vi。
可以设置名为 VPATH 的环境变量,以指定搜索源文件的目录。请参见查看路径。
缺省情况下,cscope 为当前目录中的所有 C、lex 和 yacc 源文件以及当前目录或标准位置中包含的任何头文件生成符号交叉引用表。因此,如果要浏览的程序的所有源文件均位于当前目录,并且其头文件也位于此处或标准位置,请不带参数调用 cscope:
% cscope
要浏览选定的源文件,请将这些文件的名称用作参数调用 cscope:
% cscope file1.c file2.c file3.h
有关调用 cscope 的其他方式,请参见命令行选项。
首次在要浏览的程序的源文件上使用 cscope 时,它生成符号交叉引用表。缺省情况下,此表存储在当前目录中的文件 cscope.out 中。在后续调用中,仅当源文件已被修改或源文件列表不同时,cscope 才重新生成交叉引用。重新生成交叉引用时,从旧的交叉引用复制未更改的文件的数据,这样重新生成的速度比初始生成的速度要快,并且减少了后续调用的启动时间。
现在返回到本节开头我们所承担的任务:确定导致打印错误消息 "out of storage" 的问题。已调用 cscope,并已生成交叉引用表。cscope 任务菜单显示在屏幕上。
cscope 任务菜单:
% cscope cscope Press the ? key for help Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file:
按回车键使光标在屏幕上向下移动(显示屏底部有环绕),按 ^p (Ctrl-p) 使光标向上移动;或者使用向上 (ua) 和向下 (da) 方向键。可使用以下单键命令操作菜单和执行其他任务:
|
如果您要查找的文本的第一个字符与这些命令之一匹配,您可以通过在该字符之前输入 \(反斜杠)使该命令转义。
现在将光标移至第五个菜单项 Find this text string,输入文本 "out of storage",然后按 Return 键。
cscope 函数: 请求查找文本字符串:
$ cscope cscope Press the ? key for help Find this C symbol Find this global definition Find functions called by this function Find functions calling this function Find this text string: out of storage Change this text string Find this egrep pattern Find this file Find files #including this file
cscope 查找指定的文本,找到包含该文本的一行,并报告其查找结果。
cscope 函数: 列出包含文本字符串的行:
Text string: out of storage File Line 1 alloc.c 63 (void) fprintf(stderr, "\n%s: out of storage\n", argv0); Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file:
在 cscope 显示成功搜索的结果之后,您有多个选项。您可能要更改其中一行或在编辑器中检查该行周围的代码。或者,如果 cscope 找到很多行,以致这些行的列表不能一次显示在屏幕上,您可能要查看列表的下一部分。下表显示 cscope 找到指定文本之后可用的命令:
|
同样,如果您要查找的文本的第一个字符与这些命令之一匹配,您可以通过在该字符之前输入反斜杠以避免执行命令。
现在,检查新找到的行周围的代码。输入 1(该行在列表中的编号)。系统通过文件 alloc.c 调用编辑器,并使光标位于 alloc.c 第 63 行的开头。
cscope 函数: 检查代码行:
{ return(alloctest(realloc(p, (unsigned) size))); } /* check for memory allocation failure */ static char * alloctest(p) char *p; { if (p == NULL) { (void) fprintf(stderr, "\n%s: out of storage\n", argv0); exit(1); } return(p); } ~ ~ ~ ~ ~ ~ ~ "alloc.c" 67 lines, 1283 characters
您会发现变量 p 为 NULL 时将生成错误消息。要确定在什么情况下传递给 alloctest() 的变量为 NULL,首先必须识别调用 alloctest() 的函数。
使用标准退出惯例退出编辑器。您即返回至任务菜单。现在,请在第四项 Find functions calling this function 后面键入 alloctest。
cscope 函数: 请求调用 alloctest() 的函数的列表:
Text string: out of storage File Line 1 alloc.c 63(void)fprintf(stderr,"\n%s: out of storage\n",argv0); Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: alloctest Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file:
cscope 查找并列出三个此类函数。
cscope 函数:列出调用 alloctest() 的函数:
Functions calling this function: alloctest File Function Line 1 alloc.c mymalloc 33 return(alloctest(malloc((unsigned) size))); 2 alloc.c mycalloc 43 return(alloctest(calloc((unsigned) nelem, (unsigned) size))); 3 alloc.c myrealloc 53 return(alloctest(realloc(p, (unsigned) size))); Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file:
现在希望知道哪些函数调用 mymalloc()。cscope 找到十个此类函数。它会在屏幕上列出其中九个,并指示您按空格键以查看列表的其余部分。
cscope 函数:列出调用 mymalloc() 的函数:
Functions calling this function: mymalloc File Function Line 1 alloc.c stralloc 24 return(strcpy(mymalloc (strlen(s) + 1), s)); 2 crossref.c crossref 47 symbol = (struct symbol *)mymalloc (msymbols * sizeof(struct symbol)); 3 dir.c makevpsrcdirs 63 srcdirs = (char **) mymalloc (nsrcdirs * sizeof(char*)); 4 dir.c addincdir 167 incdirs = (char **)mymalloc (sizeof(char *)); 5 dir.c addincdir 168 incnames = (char **) mymalloc(sizeof(char *)); 6 dir.c addsrcfile 439 p = (struct listitem *) mymalloc (sizeof(struct listitem)); 7 display.c dispinit 87 displine = (int *) mymalloc (mdisprefs * sizeof(int)); 8 history.c addcmd 19 h = (struct cmd *) mymalloc (sizeof(struct cmd)); 9 main.c main 212 s = mymalloc((unsigned ) (strlen(reffile) +strlen(home) + 2)); * 9 more lines - press the space bar to display more * Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file:
由于您知道错误消息 "out of storage" 在程序开始时生成,因此您可以猜测问题可能发生在函数 dispinit()(显示初始化)中。
要查看 dispinit()(列表中第七个函数),请键入 7。
cscope 函数: 在编辑器中查看 dispinit():
void dispinit() { /* calculate the maximum displayed reference lines */ lastdispline = FLDLINE - 4; mdisprefs = lastdispline - REFLINE + 1; if (mdisprefs > 9) { mdisprefs = 9; } /* allocate the displayed line array */ displine = (int *) mymalloc(mdisprefs * sizeof(int)); } ^L/* display a page of the references */ void display() { char file[PATHLEN + 1]; /* file name */ char function[PATHLEN + 1];/* function name */ char linenum[NUMLEN + 1]; /* line number */ int screenline; /* screen line number */ int width; /* source line display width */ register int i, j; "display.c" 622 lines, 14326 characters
mymalloc() 失败,原因是调用它时使用了很大的数或使用了负数。通过检查 FLDLINE 和 REFLINE 的可能值,您会发现 mdisprefs 的值为负的情况,也就是说,您尝试使用负数调用 mymalloc()。
在开窗口终端上,可能具有多个任意大小的窗口。可能由于在行过少的窗口中运行 prog 而出现错误消息 "out of storage"。换句话说,可能属于使用负数调用 mymalloc() 的情况之一。现在您要注意,将来程序在这种情况下异常终止时,在打印更有意义的错误消息 "screen too small" 之后,它会显示上述消息。按以下方式编辑函数 dispinit()。
cscope 函数: 纠正问题:
void dispinit() { /* calculate the maximum displayed reference lines */ lastdispline = FLDLINE - 4; mdisprefs = lastdispline - REFLINE + 1; if (mdisprefs > 9) { mdisprefs = 9; } /* allocate the displayed line array */ displine = (int *) mymalloc(mdisprefs * sizeof(int)); } ^L/* display a page of the references */ void display() { char file[PATHLEN + 1]; /* file name */ char function[PATHLEN + 1];/* function name */ char linenum[NUMLEN + 1]; /* line number */ int screenline; /* screen line number */ int width; /* source line display width */ register int i, j; "display.c" 622 lines, 14326 characters
您已纠正我们在本节开始时讨论的问题。现在,如果 prog 在行过少的窗口中运行,它不会简单地以无启示性的错误消息 "out of storage" 而失败。而是会在退出之前检查窗口大小并生成更富有意义的错误消息。
如上所述,缺省情况下,cscope 为当前目录中的 C、lex 和 yacc 源文件生成符号交叉引用表。即,
% cscope
等效于:
% cscope *.[chly]
我们还发现,您可以将选定的源文件的名称作为参数调用 cscope,以便浏览这些文件:
% cscope file1.c file2.c file3.h
在指定交叉引用中包含的源文件方面,cscope 为命令行选项提供了更大的灵活性。当您使用 – s 选项和任意数目的目录名称(用逗号隔开)调用 cscope 时:
% cscope– s dir1,dir2,dir3
cscope 为指定目录以及当前目录中的所有源文件生成交叉引用。要遍历在文件中列出其名称的所有源文件(文件名用空格、制表符或换行符分隔),请使用 – i 选项和包含列表的文件的名称调用 cscope:
% cscope– i file
如果源文件位于目录树中,请使用以下命令进行浏览:
% find .– name ’*.[chly]’– print | sort > file % cscope– i file
但是,如果选择此选项,cscope 将忽略在命令行上出现的任何其他文件。
cscope 使用 –I 选项的方式和 cc 使用 –I 选项的方式相同。请参见如何指定 include 文件。
您可以通过调用 – f 选项,指定一个除缺省 cscope.out 之外的交叉引用文件。对于在同一目录中保存单独的符号交叉引用文件,这是很有用的。如果两个程序在同一目录中,但不共享所有相同文件,您可能需要这样做:
% cscope– f admin.ref admin.c common.c aux.c libs.c % cscope– f delta.ref delta.c common.c aux.c libs.c
在本示例中,admin 和 delta 两个程序的源文件在同一目录中,但这两个程序由不同的文件组组成。通过在调用 cscope 时为每组源文件指定不同的符号交叉引用文件,可单独保存两个程序的交叉引用信息。
您可以使用 –pn 选项指定让 cscope 在列出搜索结果时显示文件的路径名或路径名的一部分。您为 – p 指定的数代表您要显示的路径名的最后 n 个元素。缺省值为 1,即文件本身的名称。因此,如果您的当前目录为 home/common,则命令:
% cscope– p2
使 cscope 在列出搜索结果时显示 common/file1.c、common/file2.c 等等。
如果要浏览的程序包含大量的源文件,可以使用 – b 选项,以便 cscope 在生成交叉引用后停止;cscope 不显示任务菜单。在流水线中使用 cscope -b 与 batch(1) 命令时,cscope 将在后台生成交叉引用:
% echo ’cscope -b’ | batch
生成交叉引用后,只要其间未更改源文件或源文件列表,就只需指定:
% cscope
用于要复制的交叉引用以及要以正常方式显示的任务菜单。如果希望无需等待 cscope 完成其初始处理即可继续工作,可以使用此命令序列。
-d 选项指示 cscope 不更新符号交叉引用。如果您确定未进行此类更改,则可以使用该选项以节省时间;cscope 不检查源文件是否更改。
正如我们所看到的,缺省情况下 cscope 在当前目录中搜索源文件。如果设置环境变量 VPATH,cscope 将在包含您的视图路径的目录中查找源文件。视图路径是有序的目录列表,每个目录下面具有相同的目录结构。
例如,假设您参与某个软件项目。在 /fs1/ofc 下面的目录中有一组正式源文件。每个用户都有一个起始目录 (/usr/you)。如果您更改软件系统,则 /usr/you/src/cmd/prog1 中可能只有您正在更改的文件的副本。完整程序的正式版本可在目录 /fs1/ofc/src/cmd/prog1 中找到。
假设您使用 cscope 浏览组成 prog1 的三个文件,即 f1.c、f2.c 和 f3.c。您需要将 VPATH 设置为 /usr/you 和 /fs1/ofc 并将其导出,如下所示:
在 Bourne shell 中,键入:
$ VPATH=/usr/you:/fs1/ofc; export VPATH
在 C shell 中,键入:
% setenv VPATH /usr/you:/fs1/ofc
然后创建当前目录 /usr/you/src/cmd/prog1,并调用 cscope:
% cscope
程序将定位视图路径中的所有文件。如果找到重复文件,cscope 使用其父目录在 VPATH 中较早出现的文件。因此,如果 f2.c 位于您的目录中,并且所有三个文件位于正式目录中,cscope 将从您的目录中检查 f2.c,并从正式目录中检查 f1.c 和 f3.c。
VPATH 中的第一个目录必须为您将在其中工作的目录的前缀,通常为 $HOME。VPATH 中的每个冒号分隔目录都必须是绝对目录:它应该以 / 开头。
cscope 和编辑器调用可以形成堆栈。也就是说,当 cscope 使您进入编辑器查看某个符号的引用时,如果存在另一个相关引用,您可以从编辑器内部再次调用 cscope 以查看第二个引用,而无需退出对 cscope 或编辑器的当前调用。然后,您可以通过使用相应的 cscope 命令和编辑器命令退出,执行倒退操作。
本节中的示例说明如何使用 cscope 执行以下三项任务:将常量更改为预处理程序符号、向函数增加参数以及更改变量的值。第一个示例说明了更改文本字符串的过程,这与 cscope 菜单上的其他任务稍有不同。也就是说,在您输入要更改的文本字符串之后,cscope 会提示您输入新文本,显示包含旧文本的行,并等待您指定要更改的行。
假设您要将常量 100 更改为预处理程序符号 MAXSIZE。选择第六个菜单项 Change this text string,然后输入 \100。必须使用反斜杠将 1 转义,原因是它对 cscope 具有特殊意义(菜单上的第 1 项)。现在按回车键。cscope 将提示您输入新文本字符串。键入 MAXSIZE。
cscope 函数: 更改文本字符串:
cscope Press the ? key for help Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: \100 Find this egrep pattern: Find this file: Find files #including this file: To: MAXSIZE
cscope 显示包含指定文本字符串的行,并等待您选择要更改其中文本的行。
cscope 函数: 提示要更改的行:
cscope Press the ? key for help Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: \100 Find this egrep pattern: Find this file: Find files #including this file: To: MAXSIZE
您知道列表中第 1、2 和 3 行(列出的源文件的第 4、26 和 8 行)中的常量 100 应更改为 MAXSIZE。您还知道 read.c 中的 0100 和 err.c 中的 100.0(列表中的第 4 行和第 5 行)不应更改。使用以下单键命令选择要更改的行:
|
在本例中,输入 1、2 和 3。您键入的数字不会打印在屏幕上。相反,cscope 通过在列表中您要更改的每个列表项的行号后面打印 >(大于)符号来标记这些项。
cscope 函数: 标记要更改的行:
Change "100" to "MAXSIZE" File Line 1>init.c 4 char s[100]; 2>init.c 26 for (i = 0; i < 100; i++) 3>find.c 8 if (c < 100) { 4 read.c 12 f = (bb & 0100); 5 err.c 19 p = total/100.0; /* get percentage */ Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file: Select lines to change (press the ? key for help):
现在键入 ^d 更改所选行。cscope 将显示已更改的行并提示您继续执行。
cscope 函数: 显示已更改的文本行:
Changed lines: char s[MAXSIZE]; for (i = 0; i < MAXSIZE; i++) if (c < MAXSIZE) { Press the RETURN key to continue:
当您响应此提示而按回车键时,cscope 将刷新屏幕,从而使其恢复到您选择要更改的行之前的状态。
下一步是为新符号 MAXSIZE 添加 #define。由于将要显示 #define 的头文件不在其行得到显示的文件之列,因此您必须通过键入 ! 退回到 shell。shell 提示符出现在屏幕底部。然后进入编辑器,并添加 #define。
cscope 函数: 退出到 Shell:
Text string: 100 File Line 1 init.c 4 char s[100]; 2 init.c 26 for (i = 0; i < 100; i++) 3 find.c 8 if (c < 100) { 4 read.c 12 f = (bb & 0100); 5 err.c 19 p = total/100.0; /* get percentage */ Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file: $ vi defs.h
要恢复 cscope 会话,请退出编辑器并键入 ^d 以退出 shell。
向函数增加参数包括两个步骤: 编辑函数本身,以及向代码中调用函数的各个位置增加新参数。
首先,使用第二个菜单项 Find this global definition 编辑函数。接着,找出调用函数的位置。使用第四个菜单项 Find functions calling this function 获取调用此函数的所有函数的列表。使用此列表,您可以通过分别输入每行在列表中的编号为该行调用编辑器,或通过键入 ^e 自动为所有行调用编辑器。使用 cscope 进行此类更改可确保您需要编辑的任何函数均不会被忽略。
有时,您可能要了解建议的更改如何影响代码。
假设您要更改变量或预处理程序符号的值。在这样做之前,请使用第一个菜单项 Find this C symbol 获取受到影响的引用的列表。然后使用编辑器检查每个引用。此步骤有助于预测您建议的更改的总体影响。之后,可以按相同的方式使用 cscope 来验证是否已进行更改。
缺省情况下,cscope 会调用 vi 编辑器。可以通过将首选编辑器分配给 EDITOR 环境变量并导出 EDITOR 来覆盖缺省设置,如步骤 1:设置环境中所述。但是,cscope 期望它使用的编辑器具有以下形式的命令行语法:
% editor +linenum filename
与 vi 一样。如果要使用的编辑器没有此命令行语法,则必须编写 cscope 和编辑器之间的接口。
假设您要使用 ed。由于 ed 不允许在命令行上指定行号,因此除非编写一个包含以下行的 shell 脚本,否则不能在 cscope 中使用它查看或编辑文件:
/usr/bin/ed $2
让我们将 shell 脚本命名为 myedit。现在将 EDITOR 的值设置为 shell 脚本并导出 EDITOR:
在 Bourne shell 中,键入:
$ EDITOR=myedit; export EDITOR
在 C shell 中,键入:
% setenv EDITOR myedit
当 cscope 为您指定的列表项(比如说 main.c 中的第 17 行)调用编辑器时,它将用以下命令行调用 shell 脚本:
% myedit +17 main.c
myedit 然后丢弃行号 ($1) 并使用文件名 ($2) 正确调用 ed。当然,您不会自动移动到文件的第 17 行,而必须执行适当的 ed 命令才能显示和编辑该行。