使用Scala + sbt + sbt-android-plugin开发Android应用程序

开发Android程序通常所使用的语言是Java,但Scala做为一种有希望替代Java的语言,也逐渐被越来越多的Android者所熟悉。本文简要的介绍如何使用Scalasbt工具(Simple Build Tool)及sbt-android-plugin创建,编译,安装运行一个简单的Android程序。

  1. 环境准备

    Easwy的开发环境搭建在Ubuntu 10.04.3上,所以第一步是安装Ubuntu,具体过程略过。

    然后需要下载Android SDK,这一步可以参考文档Download the Android SDK完成。

    接下来在Ubuntu里安装Sun Java JDK,如果你使用OpenJDK,可以跳过这一步。首先去掉/etc/apt/sources.list中这两行前的注释,使能Java 6源:

    $ sudo vim /etc/apt/sources.list
    deb http://archive.canonical.com/ubuntu lucid partner
    deb-src http://archive.canonical.com/ubuntu lucid partner 

    然后安装Java 6 JDK:

    $ sudo aptitude install sun-java6-jdk 
  2. 安装Scala、sbt和sbt-android-plugin

    首先安装Scala。到Scala Download Page下载Scala,Easwy下载的是Scala 2.9.1的IzPack Installer,下载后执行:

    $ java -jar scala-2.9.1.final-installer.jar 

    按提示安装即可。

    然后安装sbt。到sbt wiki上下载sbt-launch.jar,然后创建一个脚本来调用它:

    $ vim ~/bin/sbt
    java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M -jar `dirname $0`/sbt-launch.jar "$@"
    
    $ chmod u+x ~/bin/sbt 

    最后安装sbt-android-plugin。因为sbt-android-plugin还在开发中,在此Easwy直接checkout它的源代码,然后安装在本地:

    $ git clone git://github.com/jberkel/android-plugin.git
    $ cd android-plugin
    $ sbt publish-local 

    sbt在第一次运行时会下载它所依赖的包,耗时会久一些,此过程只需进行一次。

  3. 自动生成Android应用程序框架

    设置好上面的环境后,可以使用sbt-android-plugin来自动生成一个简单的Android应用程序框架。插件的作者推荐使用一个名为giter8的模板工具来生成框架。首先需要下载giter8工具:

    $ cd ~/bin
    $ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
    $ ~/bin/cs n8han/giter8 

    这几条命令会下载一些它们所需的包,需要一些时间。接下来就可以用giter8工具,根据指定的模板自动生成一个Android应用程序:

    $ ~/bin/g8 jberkel/android-app
    
    Template for Android apps in Scala
    
    package [my.android.project]: com.easwy.projects.helloworld
    name [My Android Project]: Hello World
    main_activity [MainActivity]:
    scala_version [2.9.1]:
    api_level [10]:
    useProguard [true]:
    
    Applied jberkel/android-app.g8 in hello-world 

    这条命令会提示你输入一些参数,然后根据你输入的参数生成一个Android应用程序的框架。

    事实上,此命令直接找到保存在github.com上的模板,然后根据模板生成Android应用程序的目录框架。在本例中所使用的模板是jberkel/android-app。你也可以自己创建一套模板,生成更适合自己的目录框架。

  4. 编译、安装、运行Android程序

    由上面的模板生成的Android应用程序,其实就是一个简单的“Hello World!”应用,你可以直接编译、安装、运行它。

    首先进入sbt的交互模式:

    $ cd hello-world
    $ export ANDROID_SDK_ROOT=~/bin/android-sdk-linux_86
    $ sbt

    开始编译:

    > android:package-debug 

    然后用下面的命令启动你的Android模拟器(如果你还没有模拟器,参考Managing Virtual Devices创建一个):

    > android:emulator-start api10

    上面的命令启动了名为api10的模拟器,下面安装运行我们编译出来的Hello World程序:

     > android:start-emulator 

    现在可以看到Hello World程序已经在api10 emulator中运行了。

在后续的文章中,Easwy会继续介绍使用Scala开发Android程序的心得。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

在scala中判断一个对象是否是元组(Tuple)

在scala user邮件列表中看到一个函数,判断一个对象是否是元组。从这个函数中,Easwy才了解到原来模式匹配(Pattern Match)也可以应用于正则表达式,这使得在scala中使用正则表达式(regex)更加容易了。另外,这个函数还展现了scala的另外两个特点:

  1. 尽量使用递归解决方案,而不是使用循环。这样做的优点之一是避免使用变量,优点之二是代码简洁。是否有其它优点,Easwy仍在总结。但Easwy始终有个担心,递归会不会导致效率降低?会不会有堆栈溢出风险?
  2. 使用Option类型做为函数返回值。使用Option类型的好处很明显,这样你的函数既可以返回执行失败的情况(None),也可以在执行成功时给出有用的返回值。这比使用tru/false作为返回值方便很多。

函数主体如下,为了方便讲解,在前面加了行号:


1 val Ptrn = """scala.Tuple(\d+)""".r
2
3 def checka( x: Class[ _ ]) : Option[ Int ] = x.getName match {
4      case Ptrn( i ) => Some( i.toInt )
5      case _ => { val sc = x.getSuperclass; if( sc != null ) checka( sc ) else None }
6 }
7
8 def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
     
  1. 行1定义了一个Pattern对象,可以看到,在scala中使用正则表达式非常的简单。
  2. 行3定义了一个递归函数checka(),它的参数是Class[_],返回值是Option[Int]类型。参数”Class[_]“的意思是这个函数可以接受任意Class[A]类型,也就是接受任何参数化的Class类型(Type Parameterization)。

  3. 从行3的后半句,到行5,是一个模式匹配,检查类的名字是否匹配正则表达式Ptrn。如果类名匹配Ptrn,也就是说是一个Tuple,则返回它的维数。例如,对Tuple3返回Some(3)。如果类名不匹配Ptrn,递归调用checka()检查其父类是否为Tuple,如果全部失败,则返回None。
  4. 行8定义isTuple()函数,调用checka()判断是否为Tuple。它首先会检查x是否是一个Product实例,满足时才调用checka(),否则直接返回None。

Easwy感觉checka()函数中的递归写的不是很好,在看过”The Little Schemer”后,Easwy更倾向与下面的写法:


  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }
     

下面是Easwy用来测试该函数的程序,全文如下:


object TestTuple {
  def main(args: Array[String]) {
    class ttt(a: Any, b: Any, c: Any) extends Tuple3(a, b, c)

    val test = List(new Tuple2(1, 2), new ttt(1, 2, 3), "Hello World")

    for (elem <- test)
      isTuple(elem) match {
        case None => println("Not Tuple")
        case Some(x) => println("Is Tuple" + x)
      }
  }

  val Ptrn = """scala.Tuple(\d+)""".r

  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }

  def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
}

有兴趣的朋友可以编译运行一下,体会一下scala的简捷与优雅。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

  • [scala-user] Trying to discover if a object is a tuple

scala学习:添加行号

我的第一个scala脚本完成的功能就是为文本文件添加行号,不过那个脚本中使用了变量,不太符合scala编程的思想。现在把这个脚本改造了一下,去掉var变量的使用,完全使用val值。另外,把脚本改成了需编译的程序。

LineNumber.scala程序如下:

import scala.io.Source

object LineNumber {
  def main(args: Array[String]) {
    for (file <- args) {
      println("File: " + file)
      println("-" * 32)
      for (line <- addLineNumber(file))
        print(line)
    }
  }

  def addLineNumber(fileName: String): List[String] = {
    def padding(m: Int, i: Int) = " " * (m - i.toString.length)

    val lines = Source.fromFile(fileName).getLines.toList
    val maxWidth = lines.length.toString.length

    for {
      (l, i) <- lines.zipWithIndex
      j = i + 1
      line = padding(maxWidth, j) + j + " " + l
    } yield line
  }
} 

这个程序会从命令行获取要添加行号的文件名,把加了行号的文本打印到屏幕上。

用下面的命令编译该程序:

    fsc LineNumber.scala 

然后可以用下面的命令执行该程序,输出如下:

$ scala -cp . LineNumber LineNumber.scala
File: LineNumber.scala
--------------------------------
 1 import scala.io.Source
 2
 3 object LineNumber {
 4   def main(args: Array[String]) {
 5     for (file <- args) {
 6       println("File: " + file)
 7       println("-" * 32)
 8       for (line <- addLineNumber(file))
 9         print(line)
10     }
11   }
12
13   def addLineNumber(fileName: String): List[String] = {
14     def padding(m: Int, i: Int) = " " * (m - i.toString.length)
15
16     val lines = Source.fromFile(fileName).getLines.toList
17     val maxWidth = lines.length.toString.length
18
19     for {
20       (l, i) <- lines.zipWithIndex
21       j = i + 1
22       line = padding(maxWidth, j) + j + " " + l
23     } yield line
24   }
25 }
  

学习scala有大半个月了,越来越感受到了scala的简洁与强大。

更多内容,请阅读易水博客上的其它文章。

Easwy的第一个scala脚本

正在学习scala语言,还谈不上有很深入的认识,至于为什么学习scala,最初的动力大概是它简洁的语法以及与Java的兼容性。它使用类似脚本语言的语法,实现了编译语言的执行效率,是最让Easwy心动的地方了。
看完”Programming in Scala”的前三章,参照书中的例子,我写了我的第一个scala脚本,功能很简单,它读入一个文本文件,然后把文件的内容加上行号后打印出来。
脚本程序如下:

import scala.io.Source

if (args.length > 0) {
  val lines = Source.fromFile(args(0)).getLines.toList
  val maxWidth = lines.length.toString.length
  var i = 1
  for (line <- lines) {
    val padding = maxWidth - i.toString.length
    print(" " * padding + i + " " + line)
    i += 1
  }
}
else
  println("A file name is needed")

这个脚本要使用下面的命令执行:

scala printfile.scala a-text-file-here

如果你还没有安装scala,请到scala的主页下载并安装。
2010/04/07更新
在这个脚本中使用var变量,不太符合scala的编程思想,在文章scala学习:添加行号中,Easwy将其改成符合scala方式的val形式。

Windows下cscope -q选项出错的解决

以前曾有网友留言,在windows下使用cscope时,使用-q选项出现如下提示:

D:\Temp> cscope -Rbkq
Input file specified two times.

cscope: cannot create inverted index; ignoring -q option
cscope: removed files ncscope.in.out and ncscope.po.out 

当时Easwy以为是windows版本的cscope不支持-q选项。

不过网友Liang Feng昨天留言说:

Windows版本的cscope是支持-q选项的,出现以上错误是因为没找到合适的sort命令。所以只要cscope进程的工作目录下有GNU版本的sort就可以了。注意是进程的工作目录,不是可执行程序的目录。具体原因看一下cscope的代码片段。

build.c

#ifdef WIN32
snprintf(sortcommand, sizeof(sortcommand), “set LC_ALL=C && sort -T %s %s”, tmpdir, temp1);
#else
snprintf(sortcommand, sizeof(sortcommand), “env LC_ALL=C sort -T %s %s”, tmpdir, temp1);
#endif 

于是重新做了一下试验。在安装了Cygwin后,在cygwin的shell界面中使用cscope,没有出现上面的错误提示。但如果打开windows的命令行窗口cmd.exe,在里面执行cscope,则会出现上面的错误。

在仔细比较了cygwin和cmd.exe的环境后发现,原来是Windows自带的sort.exe搞的鬼。

在cygwin的shell窗口中,PATH环境变量的设置会使其先找到GNU版的sort.exe,所以上述cscope命令执行可以成功。但在cmd.exe窗口中,PATH环境变量会使其先找到windows自带的sort.exe而不是GNU sort.exe,所以会出现上面的错误提示。

知道了问题的原因,解决方法就很简单了,我们可以写一个批处理程序,在此程序中重设PATH环境变量,使cscope使用GNU版的sort.exe。示例程序如下:

D:\Temp> type cs.bat
@echo off
set path=c:\cygwin\bin;
cscope -Rbkq 

在这个批处理里,首先把path环境变量指向c:\cygwin\bin;,我的cscope.exe和GNU版的sort.exe都在此目录中。接下来现执行cscope命令,现在它使用的就是GNU版的sort.exe了。

在此感谢网友Liang Feng,谢谢他的提醒。

更多内容,请阅读易水博客上的其它文章。

Exuberant Ctags用法:选择tag文件中所包含的tag类型

网友SamPeng问:

请问哪个参数是不解析方法中变量的。

方法中的变量纯粹是浪费tag标签。从来不用。因为一个文件里有多个同明变量

这个问题比较普遍,因此写一篇文章来解释一下。

大多数人在使用Exuberant Ctags时,一般都使用缺省的ctags -R。一般来讲,这条缺省命令已经可以满足大部分人的需求了。在某些情况下,可能对tag文件中标签的类型有特殊要求,这时就需要进行定制了。

对SamPeng所提出的问题,解决方法如下:

  1. 首先用ctags –list-kinds=<LANG>列出这个语言所支持的标签类型
  2. 然后在生成tag文件时,用ctags –<LANG>-kinds=[+|-]kinds来增加或去掉这种tag类型
  3. 需要把<LANG>换成你所用的编程语言的名字,用ctags –list-languages查看ctags支持哪些语言。

其实我们在文章vim使用进阶: 智能补全中已经用过Exuberant Ctags的定制功能了,在那篇文章里,我们使用命令

ctags -R --c++-kinds=+p --fields=+iaS --extra=+q src 

来为OmniCppComplete插件生成定制的tag文件。仔细分析一下上面这条命令,相信你对Exuberant Ctags的了解会更加深一层的。

从上面的介绍我们可以看到,Exuberant Ctags的功能非常强大(例如,甚至可以用它为任意文件提取tag,你只需要指定生成tag的规则就行了),只是大多数人并不了解。如果你对tag文件的内容有特殊的需求,建议你仔细阅读一下Exuberant Ctags的手册。你可以在这里找到我翻译的Exuberant Ctags中文手册

如何将cscope安装到指定目录

登录博客,看到joecgs问

easwy:您好,感谢您的这一系列的文章,帮助我很快地熟悉使用vim,谢谢。
我现在是用远程的shell来写程序,服务器没有安装cscope,而又不能获取root权限。所以,我想能不能通过和安装vim一样的方式来安装cscope?

随着Linux的普及,使用Linux进行软件开发的人也越来越多。而大多数公司都采用这种方式:提供一台高性能的中央服务器做为开发编译服务器,每个人登录这台服务器进行开发编译。在这种情况下,用户通常没有root权限,怎么安装应用程序呢?

其实,Linux作为老牌多用户操作系统UNIX的一个分支,这当然不是一个问题。多用户操作系统中,允许用户根据自己的喜好安装软件,是再自然不过的事情了。

做法也很简单,UNIX的程序,一般都使用autoconf来自动检测安装环境完成配置,然后再进行编译、安装,这三步就是俗称的三板斧。

在使用autoconf进行配置时,可以使用参数来更改缺省的配置,比如,通过使用–prefix参数,就可以改变程序的安装目录,还有一些其它参数,允许用户打开或关闭某些特性。每个程序所支持的参数不一定相同,可以通过./configure –help命令查看程序支持哪些参数。

回到joecgs的问题上来,如果想把cscope安装到自己的HOME目录下,其实只需要在configure时,指定–prefix=$HOME就可以了,全部命令如下:

cd cscope-15.7
./configure --prefix=$HOME
make
make install 

如果你留心一下,我们在编译vim时,也可以指定–prefix参数。其实,对绝大多数支持autoconf的程序来说,都可以用这一参数来指定你程序的安装位置,不信你试试!

在debian etch中配置subversion版本管理

以下操作如无特殊注明,均为root执行的操作。

安装软件包

首先,需要下载安装下列软件包:

  • apache2
  • apache2-common
  • apache2-utils
  • libapache2-svn
  • openssl
  • ssl-cert
  • subversion
  • subversion-tools

使用apt-get install命令安装上列软件包。

生成SSL自认证证书

如果要加密对版本服务器的访问,需要启用SSL。你可以到商业证书颁发机构去申请一个合法的证书,也可以采用自认证的方式。我们用下面的命令生成SSL自认证证书,并把它放在/etc/apache2/ssl/目录中。

mkdir /etc/apache2/ssl
/usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf /etc/apache2/ssl/apache.pem 

根据提示回答相应的信息,最后就会生成所需的证书了。

配置apache2

在apache2中启用ssl模块,使用下面的命令:

a2enmod ssl 

接下来,配置虚拟主机来使能ssl,我的apache2服务器只提供版本库的加密访问,不提供基本的http web服务,所以关闭了80端口,只开启443端口。

修改/etc/apache2/sites-available/default文件,将前两行改为:

NameVirtualHost *:443
<VirtualHost *:443> 

然后在文件中加入下面的内容:

      SSLEngine On
      SSLCertificateFile /etc/apache2/ssl/apache.pem 

上面的语句打开了SSL引擎,使用我们刚刚生成的证书文件进行认证。

default虚拟主机应该缺省已经使能了,也就是在/etc/apache2/sites-enabled/目录中已经存在一个符号链接,指向/etc/apache2/sites-available/default。如果没有的话,可以使用下面的命令使能这个虚拟主机:

a2ensite default 

现在修改文件/etc/apache2/ports.conf,把文件的内容改为:

Listen 443 

也就是我们的apache2只监听443端口。

好了,现在启动apache2:

/etc/init.d/apache2 start 

如果你没有使用ServerName配置主机名的话,会提示下面的错误,忽略即可:

Starting web server (apache2)...apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName 

在浏览器中输入如下URL,看是否能够访问:


https://127.0.0.1/

如果正常的话,你会看到一个缺省的页面,上面显示”It works!“。如果没有出现此页面,可能你的apache2配置有问题,查看一下错误日志和访问日志,以定位问题。这两个日志的缺省位置在 /var/log/apache2/。

配置subversion

首先新建一个用户svnadmin来管理版本库,使用下面的命令:

addgroup svn
adduser svnadmin –ingroup svn
adduser www-data svn 

上面的命令首先创建一个svn用户组,然后创建用户svnadmin,并把这个用户放在svn组中。第三个命令则是把www-data用户也加入到svn组,因为web server是以www-data用户在运行的,要从web server读写版本库,www-data用户必须有版本库的读写权限。

现在用su – svnadmin命令切换到svnadmin用户,我们要保证svn用户组的所有用户都具备版本库的读写权限,所以修改svnadmin的umask,修改svnadmin用户的~/.bashrc,在文件中加入下面这行:

umask 002 

退出再重新登录。这样,由svnadmin创建的版本库,就可以被所有属于svn组的用户读写了。

现在创建版本库(由用户svnadmin执行下面的命令):

svnadmin create ~/repos/svntest 

我们创建了一个名为svntest的版本库。如果你已经有cvs的版本库,想把它转到subversion上,可以安装cvs2svn软件包,用里面的工具进行转换。

配置apache2/SVN

首先,使能apache2的dav_svn模块:

a2enmod dav_svn 

我们使用基本的http认证对访问版本库的所有用户进行认证,只有认证用户才有版本库的访问权。

首先为版本库的用户生成密码文件:

htpasswd -cm /etc/svnusers xxxxx
htpasswd -m /etc/svnusers yyyyy 

上面的命令创建了密码文件/etc/svnusers,并增加两个用户xxxxx和yyyyy。

然后在/etc/apache2/sites-available/default文件中加入如下语句:

<Location /svn>
DAV svn
SVNParentPath /home/svnadmin/repos
AuthType Basic
AuthName "Subversion Auth"
AuthUserFile /etc/svnusers
require valid-user
</Location> 

这段话,对URL中指向/svn目录的访问请求,使用DAV进行访问。我们把/home/svnadmin/repos做为所有版本库的父目录,在该目录下可以增加多个版本库。另外,使用/etc/svnusers密码文件对用户进行认证。

现在,重启apache2服务:

/etc/init.d/apache2 restart 

在浏览器中输入:

https://127.0.0.1/svn/svntest 

这时,会询问你是否接受服务器的证书,选择接受,然后输入你的用户名和密码,验证通过后,在浏览器中就可以看到版本库了。

测试通过后,你就可以使用其它的subversion客户端对版本库进行访问了。

[参考文档]

Exuberant Ctags中文手册

[译序]

(Easwy) First of all, I would like to thank the author of Exuberate Ctags, Darren Hiebert,
without his help, I cannot finish the work.

翻译这个手册的想法,最初源于我在写“使用VIM开发软件项目”系列文章(现在改名为”vim使用进阶“时。当时看到大家在使用 ctags 时,
基本都是用 "ctags -R"。其实 ctags 所拥有的能力并不止这些。它的众多选项可以让你方便的控制标签文件的
内容,甚至你可以自己定义一种语言,由 ctags 为它生成标签文件 (这方面的例子,可见下面的链接:
http://ctags.sourceforge.net/EXTENDING.html)

你可以在我的BLOG上找到最新版本的译文: http://easwy.com/blog/
译文可自由使用,转载请保留译序部分。
======

NAME
ctags – 为源代码产生标签文件

SYNOPSIS
ctags [options] [file(s)]

etags [options] [file(s)]

DESCRIPTION
ctags 和 etags 程序 (下文中统称为 ctags,除非特别指明) 为文件中的各种语言对象生成一个索引
(或称为标签) 文件。标签文件允许这些项目能够被一个文本编辑器或其它工具简捷迅速的定位。一个
“标签”是指一个语言对象,它对应着一个有效的索引项 (或者换言之,这个索引项为这个对象而创建)。

可选的,ctags 能够为多种程序语言文件的语言对象信息生成可读格式的交叉索引列表。

标签索引文件被多种编辑器支持。这些编辑器允许用户通过出现在源文件中的名字定位对象,并且跳转到
定义这个名字的文件和行。在这个版本发布时,我们所知的编辑器包括:

Vi(1)及其变种(例如,Elvis, Vim, Vile, Lemmy), CRiSP, Emacs, FTE (Folding Text Editor),
JED, jEdit, Mined, NEdit (Nirvana Edit), TSE (The SemWare Editor), UltraEdit, WorkSpace,
X2, Zeus

Ctags可以为许多种不同的程序语言产生不同类型的标签。要获得完整的程序语言支持列表、它们被识别
的名字、为它们所产生的标签的的类型,请见 –list-languages 和 –list-kinds 选项。

SOURCE FILES
除非定义了 –language-force 选项,每个源文件的语言类型将根据文件名到程序语言名的映射自动选择。
每种程序语言的映射可以通过 –list-maps 选项查看,并且可以通过 –langmap 选项修改。在支持的平
台上,如果文件的名字没有映射到一种程序语言并且这个文件是可执行的,将检查文件的第一行,看该文
件是不是一种可识别编程语言的 "#!" 脚本。
缺省的,所有其它文件都被忽略。这允许在一个目录的所有文件上执行 ctags (例如,"ctags *" ),或
在整个源代码目录树的所有文件上执行 ctags (例如,"ctags -R")。因为只有那些文件名被映射到一种
程序语言的文件才会被扫描。

[ .h 扩展名的文件被映射为 C++ 文件而不是 C 文件的原因是 .h 扩展名也应用于 C++ 语言,并且把它
当做 C++ 文件对待并没有害处]

OPTIONS
尽管拥有大量的选项,ctags 设置了缺省值 (适合多数情况),因此通常不带任何选项来执行 ctags (例
如,ctags *,或ctags -R),这将为当前目录下所有可识别的源文件生成一个标签文件。下面提供的选项
仅仅为了在有特殊需要时允许用户自定义。

需注意,用来分隔单字母选项和它们参数的空格是可选的。

同样需注意,长格式选项的布尔型参数 (那些以 "–" 开头并且带 "[=yes|no]" 参数的选项) 可以被省
略,在这种情况下隐含为 "=yes" ,而 "=0" 和 "=off" 被认为是 "=no" 。

某些选项在以 etags 模式运行时或被忽略,或会起作用 (见 -e 选项)。这样的选项会特殊注明。

绝大多数选项可以出现在命令行的任意位置,只影响在该选项后面的文件。然而,少数选项必须出现在第
一个文件名之前,这样的选项也会特殊注明。
带程序语言名字的选项允许名字是大写或小写。见 –list-languages 选项,以获得内建程序语言名的完
整列表。

-a 等同于 –append

-B 使用向上查找的模式 (例如 ?pattern?)。 [在 etags 模式中忽略]

-e 使能 etags 模式,这将创建 Emacs 编辑器使用的标签文件。可选的,如果包含 "etags" 调用
ctags 时 (通过重命名,或者创建符号链接到可执行文件的方式),会使能 etags 模式。这个选项
必须出现在第一个文件名之前。

-f tagfile
使用所指定的 tagfile 做为标签文件的名字 (缺省是 "tags",或 "TAGS" 当以etags模式运行时)。
如果 tagfile 被定义成 "-",那么标签文件被输出到标准输出。如果 tagfile 存在并且它的第一
行不是有效的标签行时,ctags 将拒绝执行。如果你错误的输入了 "ctags -f *.c",这会救你一命,
不然它会把其它文件所产生的标签覆盖到你的第一个 C 文件! ctags 也会拒绝接受以一个 "-" (减
号)开头的文件名,因为这很可能是你忘记输入标签文件的名字,而本选项试图把它后面的选项做为
文件名。如果你真的想把你的输出标签文件名命名为 "-ugly",把它定义成 "./-ugly"。这个选项
必须出现在第一个文件名之前。如果这个选项出现多次,只有最后一个生效。

-F 使用向下查找的模式 (例如 /pattern/) (缺省)。 [在 etags 模式中忽略]

-h list
定义一个文件扩展名列表,以句号分隔,这些文件将做为包含文件(或头文件)被解析。要指定没有
扩展名的文件,用一个句号,后面不跟句号以外的字符 (例如,".","..x",".x.")。这个选项只
影响如何解析一种特定类型的标签的作用域 (也就是说,这些标签是全局可见,还是这些标签只在
定义它们的文件中可见);它并不把这些扩展名映射到任何特定的程序语言。任何位于非头文件中的
标签,如果不能被另外一个文件可见 (例如,链接到另外一个文件),那么它的作用域被认为局限于
该文件 (例如,static)。在头文件中的所有类型的标签,它的作用域都不会被认为局限于该文件。
如果列表中的第一个字符是加号,那么该列表中的扩展名将被加到现有列表之后;否则,该列表将
替换现有列表。另外,参见 –file-scope 选项。缺省的列表是 ".h.H.hh.hpp.hxx.h++.inc.def"。
要恢复缺省列表,使用 -h default。注意,如果此选项指定的一个扩展名还没有被映射到一种特定
的程序语言 (见上面的 SOURCE FILES),你也需要使用 –langmap 或 –language-force 选项。

-I identifier-list
定义一个标识符列表,在解析 C 和 C++ 源文件时会对这些标识符进行特殊的处理。这个选项主要
用来处理由于编译预处理宏的使用而引发的特殊情况。当列出的标识符是简单的标识符时,这些标
识符在解析源文件时会被忽略。如果一个标识符以 "+" 字符做为结尾,ctags 将忽略源文件中紧跟
在此标识符后的被括号括起来的参数列表。如果两个标识符以 "=" 字符分隔,在解析时,第一个标
识符将被第二个标识符所替代。标识符列表可以直接在命令行上提供,也可以从一个单独的文件中
读出。如果 identifier-list 的第一个字符是 "@",".",或目录分隔符 ("/" 或 ""), 或前两个
字符是驱动器的盘符 (例如,"C:"),identifier-list 参数将被解释为一个文件名,从这个文件中
读取标识符列表,每行一个标识符。否则,identifier-list 是需要被特殊处理的标识符的列表
(或一组标识符对的列表),每一个标识符被逗号或空格分开,(如果以空格分隔,需要用引号把整个
列表括起来,以使整个列表做为一个命令行参数)。可以使用多个 -I 选项。要清除已定义的标识符
列表,以 "-" 做为 identifier-list 参数。

这个特性在编译预处理宏的出现会导致句法混淆的时候非常有用。实际上,这是解决源文件中干扰
语法的宏所引发的问题的最好办法 (见下面的 CAVEATS)。下面的例子详细描述这点。

int foo ARGDECL4(void *, ptr, long int, nbytes)

在上面的例子中,宏 "ARGDECL4" 将被错误的解释为函数的名字,而不是正确的名字 "foo"。定义
-I ARGDECL4 会获得正确的结果。

/* creates an RCS version string in module */
MODULE_VERSION("$Revision: 1.41 $")

在上面的例子,宏定义看起来非常像一个函数的定义,因为它没有以分号结尾 (实际上,它后面甚
至还可以跟一个全局变量定义,这样看起来更像一个 K&R 风格的函数参数定义)。实际上,在试图
完成这个看起来像函数的定义时,可能会导致文件的其它部分被跳过。定义 -I MODULE_VERSION+
可以避免这样的问题。

CLASS Example {
// your content here
};

上面的例子使用了 "CLASS" 做为预处理宏,它在不同的平台上被扩展为不同的东西。例如,在
Win32 平台上它可能被定义为 "class __declspec(dllexport)",而在 UNIX 上简单的被定义为
"class"。通常,没有 C++ 的 "class" 关键字将导致源文件被错误的解析。定义 -I CLASS=class
能够得到正确的结果。

-L file
在文件 file 中读取需要产生标签文件的文件列表。如果 file 被指定为 "-",那么文件列表由标
准输入上读取。通过这个选项读到的文件将在命令行上给出的文件之后被处理。输入中的选项也会
被接受 (*1)。如果此选项被定义多次,只使用最后一个。注意:file 以行的方式被读取,只以换
行符做为分隔符,空格被认为是有意义的,这是为了支持文件名中包含空格的情况;如果输入中包
含选项,这会影响选项如何被解析 (*2)。

-n 同 –excmd=number。

-N 同 –excmd=pattern。

-o tagfile
同 -f tagfile。

-R 同 –recurse。

-u 同 –sort=no (也就是 "unsorted",不排序)。

-V 同 –verbose。

-w 此选项被悄悄忽略。仅为兼容 SVR4 Unix 的 ctags。

-x 打印一个表格形式的、可读的交叉索引 (xref) 文件到标准输出,而不是产生一个标签文件。输出
的信息包括:标签名字;标签类型;行号,文件名和标签所定义行的内容 (多余的空格被压缩)。不
会写标签文件,并且所有影响标签文件输出的选项都被忽略。这个特性的一个应用实例是为源文件
中定义的函数生成一个列表 (例如,ctags -x –c-kinds=f file),或为源文件中所有外部可见全
局变量生成一个列表 (例如,ctags -x –c-kinds=v –file-scope=no file)。这个选项必须出现
在第一个文件名之前。

–append[=yes|no]
指明为指定文件生成的标签是增加到标签文件已存在内容的后面,还是替换它们。此选项缺省关闭。
这个选项必须出现在第一个文件名之前。

–etags-include=file
在标签文件中包含一个到 file 的引用。此选项可以被指定任意多次。这个选项支持 Emacs 在一个
标签文件中包含另外一个标签文件的功能。[只在 etags 模式中有效]

–exclude=[pattern]
将 pattern 加到排除文件/目录列表中。这个选项可以被指定任意多次。对每一个 ctags 处理的文
件名,都会把该文件的完整路径 (例如,some/path/base.ext) 以及文件名 (例如,base.ext) 与
此选项定义的每个 pattern 进行比较,这允许 pattern 只匹配一个给定的文件名而不管其路径,
或者只匹配一个指定的路径。如果你所用的 C 编译器的运行库支持,那么 pattern 中可以包含
Unix 上通用的 shell 通配符 (不是正则表达式) (确保把这个选项的参数用引号括起来,以保证通
配符在传给 ctags 之前不会被 shell 扩展;另外,要知道通配符可以匹配 "/" 字符)。 你可以通
过检查 –version 选项的输出来确定在你的平台上 shell 通配符是否可用,(如果可用,) 在编译
的特性表中将包含 "+wildcards";否则将通过简单的文本比较来检查 pattern 与文件名的匹配。

如果 pattern 以字符 "@" 开始,那么剩余的字符串将被解释成一个文件的名字,由此文件中读取
排除模式字串,每行一个。如果 pattern 为空,排除列表将被清空。注意,在程序启动时,缺省的
排除列表包含"EIFGEN","SCCS","RCS",和"CVS",这些是在处理 –recurse 选项时不想意外进入
的目录名。

–excmd=type
定义在源文件中定位标签时所用的 EX 命令的类型。[在 etags 模式中忽略]

type 的有效值为 (用整个单词或用第一个字母都可以):

number 在标签文件中只使用行号来定位标签。这有4个优点:
1. 明显减小最终标签文件的大小。
2. 消除因为定义标签的行被修改而导致使用 pattern 查找标签失败的情况,导致模式
匹配失败 (注意,有些编辑器,例如vim, 够在很多这样的情况中恢复)。
3. 消除在查找雷同的匹配时,找到不正确的源码行的情况 (见下面的 BUGS)。
4. 在标签文件中为内容相同的行保留多个不同的表项。在 pattern 模式,重复的表项
被丢弃,因为它们产生的查找字串是相同的,保存重复的表项没有意义。

但是,这个选项有一个显著的缺点:对源文件的更改会导致标签文件中的行号记录不再对
应源文件的行号,导致跳转到某些标签时偏离标签定义位置一行或多行。大体上讲,这个
选项最好用在那些不打算更改的源文件上。选择这个选项导致下面的选项被忽略:-BF。

pattern 对所有标签只使用搜索字串,与行号不同,行号通常用于宏定义。优点是标签文件产生后,
增加或删除行不会引用 到旧的行号。

mixed 在这个模式,除少数例外,通常都使用搜索字串。对 C 语言,行号用于宏定义标签。这
是原始的 ctags 生成的缺省格式,因此,保留这个选项的缺省值。对 Fortran,common
块使用行号,因为它们的源码行通常都相同,使搜索模式在查找所有匹配时没有用处。

–extra=[+|-]flags
指定是否为特定类型的信息增加额外的标签条目。flags 参数是一组单字母标记,每个代表标签文
件中的一种额外的标签条目。如果 flags 前带 "+" 或 "-" 字符,会向当前生效的标记中加入,或
删除这些标记。否则 flags 替代当前的设置。每个标记的含义如下:

f 为每个源文件的文件名包含一个条目 (例如,"example.c"),它定位到文件的第一行。

q 为每个类成员包含一个额外的类修饰符条目 (为那些支持这类信息的编程语言;当前包括
C++, Eiffel 和 Java)。修饰符标签的实际格式取决于定义该标签的语言 (使用这种语言中
修饰符被定义的那种格式)。对 C++来说,格式为 "class::member";对 Eiffel 和 Java,
格式为 "class.member"。当标签文件中一个标签的名字出现多次时,这将更容易的定位到
特定的标签。然而需注意,这有可能使标签文件的大小不止增加一倍。

–fields=[+|-]flags
定义标签文件表项中的有效扩展字段 (见下面的 TAG FILE FORMAT,以获得更多信息)。flags 参数
是一组单个字母标记, 每一个代表一种类型的扩展字段,具有如下含义 (缺省为禁用,除非下面有
注明):

a 类成员的访问控制信息
f 作用域局部于文件 [使能]
i (关于)继承的信息
k 使用一个字符表示的标签类型 [使能]
K 标签类型的完整名称
l 包含该标签的源文件的编程语言类型
m (关于)实现的信息
n 标签出现的行号
s 标签的范围 [使能]
S 函数的指纹 (例如,原型或参数列表)
z 在 kind 字段中包含 "kind:" 关键字
t 把变量或 typedef 的类型和名字做为 "typeref:" 字段 [使能] (*3)

每个字母或字母组合的前面可以为 "+" ,表示将它加到缺省集合,或者为 "-",表示排除它。如果
既没有 "+" 前缀也没有 "-"前缀,只有在 flags 中明确列出的类型被包含在输出中 (也就是说,
覆盖缺省集合)。如果 –format=1 选项定义了,此选项被忽略。此选项缺省值为 "fkst"。

–file-scope[=yes|no]
指明作用域只在一个文件的标签 (即只在定义它们的文件中可见,不能被其它文件见到的标签,例
如 "static" 的标签) 是否被包含在输出中。又见 -h 选项。这个选项缺省使能。

–filter[=yes|no]
使 ctags 表现的像一个过滤器,由标准输入读取文件的名字,并一个文件接一个文件的输出它们的
标签。如果 –sorted 选项使能,只对文件内定义的标签排序。文件由标准输入上按行读入 (见 -L
选项的备注 ),并且这些文件在命令行及 -L 选项指定的文件之后处理。本选项使能时, -f,-o
和 –totals 选项被忽略。这个选项太过高深,缺省为禁用。这个选项必须出现在第一个文件名之前。

–filter-terminator=string
在 –filter 选项使能时,定义一个字符串,在一个文件的标签解析完后,会打印这个字符串到标
准输出。这允许读取 ctags 输出的应用程序能够确定一个文件的输出在什么时候结束。注意,如果
读入文件名是一个目录,并且 –recurse 选项使能,这个字符串只在该目录中的所有标签之后输出
一次。这个字符串总是被文件的最后一个标签的换行符隔开。这个选项太过高深,缺省为空。这个
选项必须出现在第一个文件名之前。

–format=level
改变输出标签文件的格式。当前有效的 level 只有 1 或 2。级别1指定原始的标签文件格式,级别2
指定一种新的扩展格式,里面包含扩展字段 (但使用了一种手段,使之仍后向兼容原始的 vi(1)
现)。缺省的级别为 2。这个选项必须出现在第一个文件名之前。 [在 etags 模式中忽略]

–help
向标准输出打印一个详细用法描述,然后退出。

–if0[=yes|no]
指定是否对于 "#if 0" 编译预处理分支中的非宏标签进行检查 (宏标签总被包含)。因为这种构造
是为了禁用代码,所以此选项的缺省值为 no。注意,这只是指明一种偏好,并不能保证在 "#if 0"
分支中的代码真的被跳过,因为产生标签的算法会在编译预处理条件太复杂时会解析条件编译的每
一个分支。

–<LANG>-kinds=[+|-]kinds
为特定的编程语言指定一个与语言相关的标签种类 (或类型) 的列表,这些内容会被包含在输出文
件中,在这里 <LANG> 不区分大小写,并且是内建语言名的一种 (见 –list-languages 选项以获
得完整列表)。kinds 参数是一组单字母标记,用来指明 (特定于这种语言的) 标签类型是包含在输
出中,还是不包含。这些特定标记集基于每种程序语言被识别,它们的含义以及缺省值可以由
–list-kinds 选项列出。每个字母或组合可以以 "+" 做前缀,表示在缺省值中加入它,或者以 "-"
做前缀,表示在缺省值中排除它。如果不以 "+" 或 "-" 为前缀,只有在 kinds 中明确列出的类型
被包含在输出中 (就是说,覆盖这种编程语言的缺省值)。

做为 C 语言的一个例子,要想在缺省标签类型中加入函数原型和外部变量定义,但是排除宏定义,
使用 –c-kinds=+px-d;要想只包含函数的标签,使用 –c-kinds=f。

–langdef=name
定义一种新的用户自定义语言,name,使用正则表达式进行解析。一旦被定义,name 可以在其它使
用程序语言名字的选项中被使用。这个选项的典型用法是,先定义这种语言,然后使用 –langmap
把文件名映射到它,最后使用 –regex-<LANG> 定义它的标签如何被解析。

–langmap=map[,map[...]]
控制文件名如何被映射到程序语言 (见 –list-maps 选项)。每个以逗号分隔的映射中,包含语言
的名字 (内建语言或用户自定义语言),一个冒号,以及一个文件扩展名和/或文件名模式的列表。
要定义一个文件扩展名,在此扩展名前加上句号 (例如,".c")。要定义一个文件名模式,把这个模
式用括号括起来 (例如,"([Mm]ake-file)")。如果你的 C 编译器的运行库支持,那么文件名模式
中可以包含 Unix 中通用的 shell 通配符 (确保用引号把参数括起来,以保护通配符在传给 ctags
之前不会被 shell 扩展)。 你可以通过检查 –version 选项的输出来确定在你的平台上 shell 通
配符是否可用,(如果可用,) 在编译的特性表中将包含 "+wildcards";否则将通过简单的文本比
较来检查文件名模式与文件名的匹配。要映射一个 (已经使用的) 文件扩展名,首先要取消它与其
它语言的映射关系。

如果映射的第一个字符是一个 "+",那么这个映射中的扩展名和文件名模式将加到这种语言的当前
映射中;否则,这个映射将替换当前映射。例如,为了指定只有扩展名为 .c 和 .x 的文件被当做
C 语言文件,使用 "–langmap=c:.c.x";想同时把扩展名为 .j 的文件加到 Java 语言文件,定义
"–langmap=c:.c.x,java:+.j"。为了把 makefiles (例如,文件名为 "Makefile","makefile",
或具有扩展名 ".mak") 映射到一种称为 "make" 的语言,定义 "–langmap=make:([Mm]akefile).mak"。
想映射没有扩展名的文件,定义一个句号,后面不包含句号以外字符 (例如,".","..x",".x.")。
想要为一种特定的语言清除映射 (这会防止自动为这个语言生成标签),定义一个空的扩展名列表
(例如, "–langmap=fortran:")。想为特定的语言恢复缺省的映射,将映射定义为default。想为
所有的语言恢复缺省的映射,定义 "–langmap=default"。
注意,在判别文件的程序语言时,文件扩展名先于文件名模式被判断。

–language-force=language
缺省的,ctags 自动为一个源文件选择语言,忽略那些不能确定程序语言的文件 (见上面的 SOURCE
FILES)。这个选项强制把每个文件做为指定程序语言 (不区分大小写;内建或用户自定义) 的文件,
而不是根据它们的扩展名自动为其选择语言类型。另外,特殊值 auto 表示语言类型应该被自动选
择 (这否决的本选项的有效方法)。

–languages=[+|-]list
定义一个语言列表,将为这些语言产生标签,list 包含一个逗号分隔的语言名列表 (不区分大小写;
内建或用户自定义)。如果列表的第一个语言前面不是 "+" 或 "-" 字符,在向列表增加或从列表删
除语言前,会先清空当前列表。列表的每个语言都将加入列表,直到遇到一个 "-"。因为 "+" 或
"-" 都可以在列表中出现,在这两个符号后面的语言就相应被加到列表,或从列表中去掉。这样,
使用一个新的列表替换当前列表,或者从当前列表中增加或删除语言都变得很简单。实际会生成标
签的文件列表取决于生效的语言扩展名映射 (见 –langmap 选项)。注意,所有的语言,包括用户
自定义的语言,(缺省)都是使能的,除非使用本选项禁止。列表中的语言名可以是任何内建语言或
一个先前被 –langdef 选项定义的语言。缺省值为 "all",这也是一个可被接受的参数。见
–list-languages 选项以获得一个完整的内建语言名称。

–license
向标准输出打印软件许可证信息,然后退出。

–line-directives[=yes|no]
定义 "#line" 操作符是否应该被识别。它们存在于预处理器的输出中,包括行号,可能还包括生成
这个预处理输出的源文件的名字。当选项使能时,ctags 会生成标签项目,将使用源文件的文件名
和行号,而不是它们在预处理器输出中的位置。标签文件中的实际文件名将和预处理器输出文件具
有同样的路径,这是因为它假设原始文件的位置相对于预处理器输出文件 (当然,除非 #line 操作
符定义的是绝对路径)。这个选项缺省关闭。注意:这个选项通常在和 –excmd-number (-n) 选项
一起使用才比较有用。你也需要使用 –langmap 或 –language-force 选项,如果 ctags 不能识
别预处理器输出文件的扩展名的话。

–links[=yes|no]
指示是否跟踪符号链接 (如果系统支持符号链接的话)。在禁止时,符号链接被忽略。这个选项缺省
打开。

–list-kinds[=language|all]
列出指定语言或全部语言所能够识别的标签种类,然后退出。在标签文件中,每个种类的标签都用
一个单字母的标记来表示,它也用于 –<LANG>-kinds 选项,用以过滤在输出中所包含的标签种类。
注意,有些语言和/或标签种类可能用正则表达式来实现,如果正则表达式支持没有编译到 ctags
中,可能无法支持 (见 –regex-<LANG> 选项)。列出的每个种类都是使能的,除非后面跟有 "[off]"。

–list-maps[=language|all]
列出指定的语言或全部语言的文件扩展名和文件名模式,然后退出。这些扩展名以及文件名模式把
一个文件与特定程序语言相关联。见上面的 –langmap 选项,以及 SOURCE FILES。

–list-languages
列出 ctags 能够识别的程序语言名,然后退出。这些程序语言名称不区分大小写,可以用在
–language-force,–languages,–<LANG>-kinds 和–regex-<LANG> 选项。

–options=file
从 file 中读取附加选项。做为一种特殊情形,如果 –options=NONE 做为命令行的第一个选项,
它将禁止自动从文件或环境变量 (见 FILES) 中读取任何配置选项。

–recurse[=yes|no]
递归进入文件列表中所遇到的目录。如果给出的文件列表为空,并且没有使用 -L 指定文件列表,
那么当前目录被使用 (也就是 ".")。符号链接将被跟踪。如果你不喜欢这样的处理,或者显式的指
定文件列表,或者通过管道将 find(1) 的输出传给 "ctags -L-"。注意,这个选项当前并不能支持
所有的平台。如果 –help 选项的输出中包含本选项,说明它可用。又见 –exclude 选项,以限制
递归行为。

–regex-<LANG>=/regexp/replacement/[kind-spec/][flags]
/regexp/replacement/ 定义一个正则表达式的替换串,与 sed 的 substitution 命令风格类似,
使用它为映射到 <LANG> 语言 (不区分大小写;内建程序语言,或用户自定义程序语言) 的源文件
产生标签。正则表达式 regexp 定义一个扩展正则表达式 (主要为 egrep(1) 所用),它用来定位包
含标签的一个源码行,使用 t 来定义 tab 字符。当一个匹配行被发现,将会生成一个名为
replacement 的标签,replacement 通常会包含特殊的前向引用 1 到 9,以引用 regexp 中的子
表达式。选项参数中的 "/" 分隔字符实际上可以换成任何字符。注意,不管使用哪个分隔符,如果
它在参数中不做分隔用,必须使用 "" 字符对其进行转义。
本选项定义的正则表达式会被添加到这种语言当前的正则表达式列表中,除非省略选项参数,这种
情况当前列表会被清空。
除非由 flags 做出更改,否则 regexp 被作为 Posix 扩展正则表达式解析。对于每个匹配行,
replacement 应该被扩展为一个非空字符串,不然的话会产生一条告警信息。一个可选的标签类型
定义可以跟在 replacement 后面,它用来指定在标签的 "kind" 扩展字段里写入的类型 (见下面的
TAG FILE FORMAT)。kind-spec 的完整格式是一个单个字母,一个逗号,一个名字 (不包含空格),
一个逗号,一个描述,后面跟分隔符,这个格式定义了类型及其文本描述的长、短形式 (使用
–list-kinds 可以显示出来)。类型的名字和/或类型的描述可以被省略。如果 kind-spec 被省略,
它的缺省值为 "r,regex"。最后,flags 是一个或多个单字母字符,对 regexp 的解析有下列影响:

b 此模式被解析为 Posix 的基本正则表达式

e 此模式被解析为 Posix 的扩展正则表达式 (缺省)

i 此模式以忽略大小写的方式被应用

注意,这个选项只在 ctags 编译时加入正则表达式支持时才有效,这取决于你所用的平台。你可通
过检查 –version 选项的输出,以确定编译时是否加入正则表达式支持,(如果支持),输出的编译
特性列表中将包含 "+regex"。

欲获得 ctags 使用的正则表达式的详细信息,见 regex(5,7) 的手册页,或见 regex 的 GUU info
文档 (例如,"info regex")。

–sort[=yes|no|foldcase]
指明标签文件是否按标签名排序 (缺省为排序)。注意,原始的 vi(1) 要求排序标签。foldcase 指
定不区分大小写排序 (或大小写合并排序)。
对大小写合并排序进行快速二叉树查找需要由使用标签文件的工具提供特殊支持,例如 ctags 的
readtags 库,或 Vim 6.2 以上版本 (使用 "set ignorecase")。这个选项必须出现在第一个文件
名之前。[在 etags 模式中忽略]

–tag-relative[=yes|no]
指明标签文件中记录的名字路径应该相对于标签文件所在的目录,而不是相对于当前目录,除非命
令行所指定的文件名使用绝对路径。这个选项必须出现在第一个文件名之前。当运行在 etags 模式
时,缺省为 yes (见 -e 选项),否则缺省值为 no。

–totals[=yes|no]
打印在本次 ctags 运行期间所读入源文件以及所写标签的统计信息。这个选项缺省关闭。这个选项
必须出现在第一个文件名之前。

–verbose[=yes|no]
使能详细模式。打印处理选项的信息,以及在 ctags 处理每个文件时打印一条简洁的消息,描述采
取什么样的动作。通常,ctags 在配置文件中的选项 (见下面的 FILES) 以及 CTAGS 环境变量被读
入前,不会读取命令行参数。然而,如果本选项是命令行的第一个选项,它将在配置文件、CTAGS
环境变量,以及命令行上的每个选项被读入前生效。缺省值为 no。

–version
打印 ctags 的版本信息,然后退出。信息中始终包含字符串 "Exuberant Ctags"。

OPERATIONAL DETAILS
在 ctags 依次处理每个文件时,它试图挨顺序进行以下三步测试来确定文件的程序语言类型:是否文件
的扩展名被映射到一种程序语言,是否文件名匹配一个映射到程序语言的 shell 模式,最后判断是否该
文件可执行并且它的第一行使用 Unix 风格的 "#!" 定义了一个解释器 (如果平台支持的话)。如果程序
语言确定了,这个文件会被打开,接下来相应的语言解析器被调用,以解析当前打开的文件。解析器解析
整个文件,并且在标签文件中为每个语言对象增加一个条目。见下面的 TAG FILE FORMAT,以获得这些条
目的详细信息。

这个 ctags 实现不像旧有的实现那样,对 C 代码的格式有要求。旧的实现往往依赖特殊的预定格式来帮
助它解决编译预处理条件引发的困难。

通常,ctags 试图更巧妙的处理翻译预处理操作符。如果在一条定义了标签的语句中遇到一个编译预处理
条件,ctags 只跟随此条件的第一个分支 (除非第一分支是 "#if 0" ,在这种情况下,只跟随剩下的唯
一一条分支)。这样做的原因是 (如果) 跟随不只一条分支会导致语法不明确,就像下面例子:

#ifdef TWO_ALTERNATIVES
struct {
#else
union {
#endif
short a;
long b;
}

不能同时跟随两个分支,否则括号会不匹配,ctags 将不能识别语法。

由于这些条件很复杂,并且 (分支) 相互排斥,如果这种办法不能正确的解析一个文件,ctags 将重新尝
试另一种不同的方法,这种方法不并选择性的跟随条件预处理分支,而是在 #if 条件分支导致大括号不
匹配时,通过位于第 1 列的大括号 ("}") 来做为一个代码块结束的指示。

Ctags 也尝试对括在两个圆括号中的参数列表进行特殊处理,以接受下面这样的条件结构:

extern void foo __ARGS((int one, char two));

任何紧挨着 "((" 的名字都被自动忽略,而使用在它之前的名字。

C++ 的运算符定义会被特殊处理。为了对所有种类的运算符 (重载或转换) 保持一致,在标签文件中,所
有运算符的名字总是以 "operator " 为前缀 (也就是说,即使实际的运算符定义被写成 "operator<<")。

在创建标签文件,或向标签文件中添加标签后,将根据标签的名字进行排序,删除相同的标签行。

TAG FILE FORMAT
在不以 etags 模式运行时,标签文件中的每个表项占单独的一行,通常每个看起来都如同这样:

tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields

字段以及分隔符定义如下:

1. 标签名
2. 单个 tab 字符
3. 文件名,这个标签对应的对象在此文件中定义
4. 单个 tab 字符
5. 在文件中定位此标签的 EX 命令;通常是一个搜索模式 (/pattern/ 或 ?pattern?) 或行号 (见
–excmd)。标签文件格式 2 (见 –format)
在某些情况下扩展了这个 EX 命令,在紧挨着此 EX 命令后面的 EX 命令注释里嵌入了一套扩展
字段 (在下面描述),这样使它仍能向下兼容原始的 vi(1) 实现。

基于内部使用的目的,会向标签文件中写入一些特殊的标签。这些标签的构成使它们始终被排序到文件的
最前面。因此,这些标签的前两个字符被用做标签文件的幻数,以确定正在写入的是一个有效的标签文件,
而不是一个源文件。

注意,记录在标签文件的每个源文件名,和命令行上定义的完全相同。因此,如果你在命令行上指定的是
相对于当前目录的路径的话,它们在标签文件中以同样的方式被记录。然而,见 –tag-relative 选项,
以更改此设置。

如上所说,扩展字段作为注释增加在 EX 命令之后,是以 tab 字符分隔的 "关键字-值" 组合。这些 "关
键字-值" 组合一般为 "key:value" 的格式。它们是否在标签文件中出现,由 –fields 选项控制。可能
的关键字以及它们值的含义见下:

access 指明类成员的可见范围,它的值特定于程序语言。

file 指明此标签只在文件中可见。这个关键字没有相应的值。

kind 指明标签的类型,或种类。它的值要么是上面 –<LANG>-kinds 选项所描述的相应单字母标
志,要么是全名。此字段的关键字部分允许被省略 (实际上,这是缺省设置)。由 –fields
选项控制它的表现。

implementation
如果存在,它指明一个函数或类具有实现的限制 (抽象与具体),它的值特定于程序语言
(对 C++ 而言是 "virtual" 或 "pure virtual";对 Java 而言是 "abstract").

inherits 如果存在,值为一个逗号分隔的类列表,这个类源于列表中的类 (也就是说,它由列表中的
类继承而来)。

signature 如果存在,它的值是函数指纹,表示方法与程序语言相关。完整的函数指纹定义了此函数的
返回类型及它的参数列表格式。这个扩展字段当前只支持基于 C 的程序语言,并且不包括
返回类型。

另外,标签作用域有关的信息也 (在标签文件中) 可用,它的关键字部分是程序语言中的构造名称,它的
值是程序中此构造的名字。作用域表项指明定义此标签的范围。例如,C 语言的结构成员所生成的标签,
会包含一个作用域,看起来像这样 "struct:myStruct"。

HOW TO USE WITH VI
Vi 缺省的期望当前工作目录中有一个名为 "tags" 的标签文件。一旦生成了标签文件,下面的命令执行
按标签索引的特性:

vi -t tag 启动 vi 并且把光标定位在 "tag" 定义所在的文件和行上。

:ta tag 查找一个标签。

Ctrl-] 查找光标下的标签

Ctrl-T 返回跳转到标签前的前一次位置 (不是所有实现都支持)。

HOW TO USE WITH GNU EMACS
Emacs 缺省的期望当前工作目录中有一个名为 "TAGS" 的标签文件。一旦生成了标签文件,下面的命令执
行按标签索引的特性:

M-x visit-tags-table <RET> FILE <RET>
选择所使用的标签文件 "FILE"。

M-. [TAG] <RET>
查找第一个 TAG 定义。缺省的标签是光标下的标识符。

M-* 跳回你先前调用 "M-." 的位置

C-u M-. 查找前一次所查找的标签的下一个定义

阅读 Emacs info 文档的 Tags 主题,以了解更多命令。

HOW TO USE WITH NEDIT
NEdit 的 5.1 及以后版本可以处理新的扩展标签文件格式 (见 –format)。要使 NEdit 使用标签文件,
选择 "File->Load Tags File"。要跳转到一个标签的定义,高亮这个标签,然后按 Ctrl-D。NEdit 5.1
可以从不同的目录读取多个标签文件。设置 X 资源中的 nedit.tagFile 为标签文件的名字,可以使
NEdit 在启动时自动加载这个标签文件。

CAVEATS
由于 ctags 既不是一个预处理器也不是一个编译器,使用预处理宏会使 ctags 漏掉标签或者错误的生成
不正确的标签。虽然 ctags 已经被设计成处理一些的通用情况,但这依旧是提交最多的问题。特别是使
用预处理构造改变 C 的语法结构时,会欺骗 ctags。你可以通过使用 -I 选项避开很多这样的问题。

(*4)
White space is treated as a separator for file names and options read from list files,
specified using the -L option, and in filter mode (specified using the –filter option).
Therefore, it is not currently possible to supply file names or other options contain-ing
embedded white space (spaces, etc.) through these options.

注意,当 ctags 使用模式字串来定位标签时 (见 –excmd 选项),如果另外一个源码行和包含这个标签
的行完全相同,你的编辑器完全有可以跳到错误的行上。下面的例子说明这种情况:

int variable;

/* … */
void foo(variable)
int variable;
{
/* … */
}

取决于你所使用的编辑器,以及你在代码中位置,搜索模式可能会在查找到真正的全局变量定义之前,查
找到 foo() 的局部参数声明,因为这两行完全相同 (因此它们的搜索模式也相同)。这可以通过使用
–excmd=n 来避免。

BUGS
Ctags 具有比 ls(1) 更多的选项.

当解析一个 C++ 成员函数定义时 (例如,"className::function"),ctags 不能确定域分隔符是一个类
名分隔符还是一个 namespace 分隔符,总是把它做一个类名放在扩展字段的作用域部分。另外,如果一
个 C++ 函数定义在类定义外面 (通常都是这样),函数定义中包含的访问限定符 (即 public,protected
或 private) 以及实现信息 (例如 virtual,pure virtual) 在为这个函数生成标签时无法知道。然而,
对于原型,这些信息可用 (例如,–c++-kinds+=p)。

没有为继承自一个类的语言对象产生标签。

ENVIRONMENT VARIABLES
CTAGS 如果这个环境变量存在,在 ctags 启动时会从此环境变量中读取缺省选项,读取发生在下面
FILES 一节中列出的配置文件之后,但在命令行选项之前。命令行中出现的选项会覆盖此环境变
量中的选项。只从这个环境变量中读取选项值。注意,环境变量中的所有空格都被认为是分隔符,
这样传递一个包含空格的选项参数是不可能的。如果这会导致问题,改用配置文件。

ETAGS 与上面的 CTAGS 类似,如果存在,它会在 etags 启动时读取。如果这个环境变量未找到,etags
将尝试改用 CTAGS 环境变量。

TMPDIR 在支持 mkstemp() 的类 Unix 主机上,这个环境变量的值定义了存放临时文件的目录。这在临
时文件太大导致缺省临时文件目录所在的分区无法装下它时比较有用。ctags 只在下列情况时创
建临时文件: (1) 生成一个 emacs 格式的标签文件, (2) 标签文件被输出到标准输出,
(3) 它被编译成使用内部的排序算法来排序标签文件,而不是使用操作系统的 sort 工具。如果
使用系统的 sort 工具,它通常也会使用这个变量。如果 ctags 是 setuid 的,TMPDIR 的值将
被忽略。

FILES
/ctags.cnf (只在 MSDOS,MSWindows)
/etc/ctags.conf
/usr/local/etc/ctags.conf
$HOME/.ctags (在 MSDOS, MSWindows 上是 $HOME/ctags.cnf)
.ctags (在 MSDOS,MSWindows上是 ctags.cnf)
如果这些配置文件中任一个存在,每个都应该包含一个缺省的选项集合,在 ctags 启动时会按列
出的顺序读入这些选项,读入的时刻发生在读取 CTAGS 环境变量以及命令行选项之前。这使得设
置适用于整个系统、每个人或基于项目的缺省选项值成为可能。在编译 ctags 时为它指定一个额
外的配置文件是可能的,它会在上面列出文件被读入之前读取,在 –version 选项输出中的
"custom-conf" 指示这个特性可用。CTAGS 环境变量及命令行中选项会覆盖这些文件中定义的选
项。只从这些文件中读入选项值。注意,选项文件按行读入,行内的空格有效 (因为不能像 shell
那样使用引号)。文件的每行被当做一个命令行参数 (就像它们被单引号括起来一样)。因此,使
用换行符做为命令行参数的分隔标志。

tags ctags 生成的缺省标签文件。

TAGS etags 生成的缺省标签文件。

SEE ALSO
Exuberant Ctags 的官方网站在:

http://ctags.sourceforge.net

参阅 ex(1),vi(1),elvis,或更好的,vim,ctags 的正式编辑器。欲获得 vim 的更多信息,请到
VIM 的 web 网站:

http://www.vim.org/

AUTHOR
Darren Hiebert <dhiebert at users.sourceforge.net>
http://DarrenHiebert.com/

MOTIVATION
"Think ye at all times of rendering some service to every member of the human race."

"All effort and exertion put forth by man from the fullness of his heart is worship, if it
is prompted by the highest motives and the will to do service to humanity."

— From the Baha’i Writings

CREDITS
这个版本的 ctags 最初继承了 Steve Kirkendall <kirkenda@cs.pdx.edu> 的 ctags 并从中获取灵感,
他的 ctags 伴随 Elvis vi 变种发行 (虽然事实上没有使用一行原来的代码)。

荣誉也要归于 Bram Moolenaar <Bram@vim.org>,vim 的作者,他花费如此多的时间和精力开发这个编辑
器 (服务于他人) 以及帮助乌干达的孤儿。

名为 "HOW TO USE WITH GNU EMACS" 的一节是从 GNU etags 的 info 文档中 "偷来" 的。

Darren Hiebert Version 5.6 CTAGS(1)

译者注:

(*1) 原文为 "Options all also accepted in this input" 。经过与作者沟通,本句应为 "Options also
accepted in this input"。意思是“在输入中的选项也会被接受”,也就是说,可以在 file 中加入选项,
这些选项将会应用在其后出现的文件上。这提供了一种非常便捷的方式,允许用户为不同的文件指定不同的
选项。

(*2) 这句话意思是,因为行内的空格被视为有意义的,所以不能再以空格来分隔各个选项,而应该以换行符来分
隔多个选项。

(*3) 按译者的理解,在使用类如 struct,union 这样的复杂数据类型进行类型定义 (typedef) 或直接定义变量
时,会为之增加 "typeref:" 字段。下面两个例子中的代码生成的标签文件就包含此字段:
/* 定义类型 */
typedef struct abc
{
int a;
char b;
} abc;

/* 直接定义变量 */
union {
TEST1,
TEST2
} test;

(*4) 这段话与前面的选项解释相抵触,正在与作者沟通。

[译文版本]
27Apr07, easwy, initial version
28Apr07, easwy, reformat it