added ctags 5.6 from http://ctags.sourceforge.net
Integration patch from kaliber as of bug #1127, with a minor fix. Thanks! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20438 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -21,7 +21,7 @@ if $(INCLUDE_GPL_ADDONS) = 1 {
|
||||
}
|
||||
|
||||
BEOS_BIN = "[" addattr alert arp basename bc beep cat catattr chgrp chmod chop chown cksum clear
|
||||
clockconfig cmp comm cp copyattr csplit cut date dc dd desklink df diff dircolors dirname driveinfo
|
||||
clockconfig cmp comm cp copyattr csplit ctags cut date dc dd desklink df diff dircolors dirname driveinfo
|
||||
dstcheck du echo eject env error expand expr factor false fdinfo ffm find finddir fortune ftp funzip gawk
|
||||
$(X86_ONLY)gdb grep groups gzip head hey id ideinfo idestatus ifconfig iroster isvolume
|
||||
join keymap kill less lessecho lesskey link listarea listattr listdev listimage
|
||||
|
||||
@@ -151,6 +151,7 @@ SubInclude HAIKU_TOP src bin bison ;
|
||||
SubInclude HAIKU_TOP src bin chkbfs ;
|
||||
SubInclude HAIKU_TOP src bin compress ;
|
||||
SubInclude HAIKU_TOP src bin coreutils ;
|
||||
SubInclude HAIKU_TOP src bin ctags ;
|
||||
SubInclude HAIKU_TOP src bin desklink ;
|
||||
SubInclude HAIKU_TOP src bin diffutils ;
|
||||
SubInclude HAIKU_TOP src bin findutils ;
|
||||
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
--blank-before-sizeof
|
||||
--blank-lines-after-procedures
|
||||
--brace-indent0
|
||||
--braces-after-if-line
|
||||
--braces-on-struct-decl-line
|
||||
--break-after-boolean-operator
|
||||
--case-brace-indentation0
|
||||
--case-indentation0
|
||||
--comment-indentation0
|
||||
--continuation-indentation4
|
||||
--declaration-comment-column0
|
||||
--declaration-indentation0
|
||||
--dont-break-function-decl-args
|
||||
--dont-break-procedure-type
|
||||
--dont-line-up-parentheses
|
||||
--honour-newlines
|
||||
--indent-level4
|
||||
--line-length80
|
||||
--no-blank-lines-after-commas
|
||||
--space-after-cast
|
||||
--space-after-for
|
||||
--space-after-if
|
||||
--space-after-procedure-calls
|
||||
--space-after-while
|
||||
--space-special-semicolon
|
||||
--start-left-side-of-comments
|
||||
--struct-brace-indentation4
|
||||
--tab-size8
|
||||
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,386 @@
|
||||
<!-- $Id: EXTENDING.html,v 1.9 2002/09/04 01:17:32 darren Exp $ -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Exuberant Ctags: Adding support for a new language</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>How to Add Support for a New Language to Exuberant Ctags</h1>
|
||||
|
||||
<p>
|
||||
<b>Exuberant Ctags</b> has been designed to make it very easy to add your own
|
||||
custom language parser. As an exercise, let us assume that I want to add
|
||||
support for my new language, <em>Swine</em>, the successor to Perl (i.e. Perl
|
||||
before Swine <wince>). This language consists of simple definitions of
|
||||
labels in the form "<code>def my_label</code>". Let us now examine the various
|
||||
ways to do this.
|
||||
</p>
|
||||
|
||||
<h2>Operational background</h2>
|
||||
|
||||
<p>
|
||||
As ctags considers each file name, it tries to determine the language of the
|
||||
file by applying the following three tests in order: if the file extension has
|
||||
been mapped to a language, if the file name matches a shell pattern mapped to
|
||||
a language, and finally if the file is executable and its first line specifies
|
||||
an interpreter using the Unix-style "#!" specification (if supported on the
|
||||
platform). If a language was identified, the file is opened and then the
|
||||
appropriate language parser is called to operate on the currently open file.
|
||||
The parser parses through the file and whenever it finds some interesting
|
||||
token, calls a function to define a tag entry.
|
||||
</p>
|
||||
|
||||
<h2>Creating a user-defined language</h2>
|
||||
|
||||
<p>
|
||||
The quickest and easiest way to do this is by defining a new language using
|
||||
the program options. In order to have Swine support available every time I
|
||||
start ctags, I will place the following lines into the file
|
||||
<code>$HOME/.ctags</code>, which is read in every time ctags starts:
|
||||
|
||||
<code>
|
||||
<pre>
|
||||
--langdef=swine
|
||||
--langmap=swine:.swn
|
||||
--regex-swine=/^def[ \t]*([a-zA-Z0-9_]+)/\1/d,definition/
|
||||
</pre>
|
||||
</code>
|
||||
The first line defines the new language, the second maps a file extension to
|
||||
it, and the third defines a regular expression to identify a language
|
||||
definition and generate a tag file entry for it.
|
||||
</p>
|
||||
|
||||
<h2>Integrating a new language parser</h2>
|
||||
|
||||
<p>
|
||||
Now suppose that I want to truly integrate compiled-in support for Swine into
|
||||
ctags. First, I create a new module, <code>swine.c</code>, and add one
|
||||
externally visible function to it, <code>extern parserDefinition
|
||||
*SwineParser(void)</code>, and add its name to the table in
|
||||
<code>parsers.h</code>. The job of this parser definition function is to
|
||||
create an instance of the <code>parserDefinition</code> structure (using
|
||||
<code>parserNew()</code>) and populate it with information defining how files
|
||||
of this language are recognized, what kinds of tags it can locate, and the
|
||||
function used to invoke the parser on the currently open file.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The structure <code>parserDefinition</code> allows assignment of the following
|
||||
fields:
|
||||
|
||||
<code>
|
||||
<pre>
|
||||
const char *name; /* name of language */
|
||||
kindOption *kinds; /* tag kinds handled by parser */
|
||||
unsigned int kindCount; /* size of `kinds' list */
|
||||
const char *const *extensions; /* list of default extensions */
|
||||
const char *const *patterns; /* list of default file name patterns */
|
||||
parserInitialize initialize; /* initialization routine, if needed */
|
||||
simpleParser parser; /* simple parser (common case) */
|
||||
rescanParser parser2; /* rescanning parser (unusual case) */
|
||||
boolean regex; /* is this a regex parser? */
|
||||
</pre>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <code>name</code> field must be set to a non-empty string. Also, unless
|
||||
<code>regex</code> is set true (see below), either <code>parser</code> or
|
||||
<code>parser2</code> must set to point to a parsing routine which will
|
||||
generate the tag entries. All other fields are optional.
|
||||
|
||||
<p>
|
||||
Now all that is left is to implement the parser. In order to do its job, the
|
||||
parser should read the file stream using using one of the two I/O interfaces:
|
||||
either the character-oriented <code>fileGetc()</code>, or the line-oriented
|
||||
<code>fileReadLine()</code>. When using <code>fileGetc()</code>, the parser
|
||||
can put back a character using <code>fileUngetc()</code>. How our Swine parser
|
||||
actually parses the contents of the file is entirely up to the writer of the
|
||||
parser--it can be as crude or elegant as desired. You will note a variety of
|
||||
examples from the most complex (c.c) to the simplest (make.c).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When the Swine parser identifies an interesting token for which it wants to
|
||||
add a tag to the tag file, it should create a <code>tagEntryInfo</code>
|
||||
structure and initialize it by calling <code>initTagEntry()</code>, which
|
||||
initializes defaults and fills information about the current line number and
|
||||
the file position of the beginning of the line. After filling in information
|
||||
defining the current entry (and possibly overriding the file position or other
|
||||
defaults), the parser passes this structure to <code>makeTagEntry()</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Instead of writing a character-oriented parser, it may be possible to specify
|
||||
regular expressions which define the tags. In this case, instead of defining a
|
||||
parsing function, <code>SwineParser()</code>, sets <code>regex</code> to true,
|
||||
and points <code>initialize</code> to a function which calls
|
||||
<code>addTagRegex()</code> to install the regular expressions which define its
|
||||
tags. The regular expressions thus installed are compared against each line
|
||||
of the input file and generate a specified tag when matched. It is usually
|
||||
much easier to write a regex-based parser, although they can be slower (one
|
||||
parser example was 4 times slower). Whether the speed difference matters to
|
||||
you depends upon how much code you have to parse. It is probably a good
|
||||
strategy to implement a regex-based parser first, and if it is too slow for
|
||||
you, then invest the time and effort to write a character-based parser.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A regex-based parser is inherently line-oriented (i.e. the entire tag must be
|
||||
recognizable from looking at a single line) and context-insensitive (i.e the
|
||||
generation of the tag is entirely based upon when the regular expression
|
||||
matches a single line). However, a regex-based callback mechanism is also
|
||||
available, installed via the function <code>addCallbackRegex()</code>. This
|
||||
allows a specified function to be invoked whenever a specific regular
|
||||
expression is matched. This allows a character-oriented parser to operate
|
||||
based upon context of what happened on a previous line (e.g. the start or end
|
||||
of a multi-line comment). Note that regex callbacks are called just before the
|
||||
first character of that line can is read via either <code>fileGetc()</code> or
|
||||
using <code>fileGetc()</code>. The effect of this is that before either of
|
||||
these routines return, a callback routine may be invoked because the line
|
||||
matched a regex callback. A callback function to be installed is defined by
|
||||
these types:
|
||||
|
||||
<code>
|
||||
<pre>
|
||||
typedef void (*regexCallback) (const char *line, const regexMatch *matches, unsigned int count);
|
||||
|
||||
typedef struct {
|
||||
size_t start; /* character index in line where match starts */
|
||||
size_t length; /* length of match */
|
||||
} regexMatch;
|
||||
</pre>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The callback function is passed the line matching the regular expression and
|
||||
an array of <code>count</code> structures defining the subexpression matches
|
||||
of the regular expression, starting from \0 (the entire line).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Lastly, be sure to add your the name of the file containing your parser (e.g.
|
||||
swine.c) to the macro <code>SOURCES</code> in the file <code>source.mak</code>
|
||||
and an entry for the object file to the macro <code>OBJECTS</code> in the same
|
||||
file, so that your new module will be compiled into the program.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This is all there is to it. All other details are specific to the parser and
|
||||
how it wants to do its job. There are some support functions which can take
|
||||
care of some commonly needed parsing tasks, such as keyword table lookups (see
|
||||
keyword.c), which you can make use of if desired (examples of its use can be
|
||||
found in c.c, eiffel.c, and fortran.c). Almost everything is already taken care
|
||||
of automatically for you by the infrastructure. Writing the actual parsing
|
||||
algorithm is the hardest part, but is not constrained by any need to conform
|
||||
to anything in ctags other than that mentioned above.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There are several different approaches used in the parsers inside <b>Exuberant
|
||||
Ctags</b> and you can browse through these as examples of how to go about
|
||||
creating your own.
|
||||
</p>
|
||||
|
||||
<h2>Examples</h2>
|
||||
|
||||
<p>
|
||||
Below you will find several example parsers demonstrating most of the
|
||||
facilities available. These include three alternative implementations
|
||||
of a Swine parser, which generate tags for lines beginning with
|
||||
"<CODE>def</CODE>" followed by some name.
|
||||
</p>
|
||||
|
||||
<code>
|
||||
<pre>
|
||||
/***************************************************************************
|
||||
* swine.c
|
||||
* Character-based parser for Swine definitions
|
||||
**************************************************************************/
|
||||
/* INCLUDE FILES */
|
||||
#include "general.h" /* always include first */
|
||||
|
||||
#include <string.h> /* to declare strxxx() functions */
|
||||
#include <ctype.h> /* to define isxxx() macros */
|
||||
|
||||
#include "parse.h" /* always include */
|
||||
#include "read.h" /* to define file fileReadLine() */
|
||||
|
||||
/* DATA DEFINITIONS */
|
||||
typedef enum eSwineKinds {
|
||||
K_DEFINE
|
||||
} swineKind;
|
||||
|
||||
static kindOption SwineKinds [] = {
|
||||
{ TRUE, 'd', "definition", "pig definition" }
|
||||
};
|
||||
|
||||
/* FUNCTION DEFINITIONS */
|
||||
|
||||
static void findSwineTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
/* Look for a line beginning with "def" followed by name */
|
||||
if (strncmp ((const char*) line, "def", (size_t) 3) == 0 &&
|
||||
isspace ((int) line [3]))
|
||||
{
|
||||
const unsigned char *cp = line + 4;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, SwineKinds, K_DEFINE);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* SwineParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "swn", NULL };
|
||||
parserDefinition* def = parserNew ("Swine");
|
||||
def->kinds = SwineKinds;
|
||||
def->kindCount = KIND_COUNT (SwineKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findSwineTags;
|
||||
return def;
|
||||
}
|
||||
</pre>
|
||||
</code>
|
||||
|
||||
<p>
|
||||
<pre>
|
||||
<code>
|
||||
/***************************************************************************
|
||||
* swine.c
|
||||
* Regex-based parser for Swine
|
||||
**************************************************************************/
|
||||
/* INCLUDE FILES */
|
||||
#include "general.h" /* always include first */
|
||||
#include "parse.h" /* always include */
|
||||
|
||||
/* FUNCTION DEFINITIONS */
|
||||
|
||||
static void installSwineRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language, "^def[ \t]*([a-zA-Z0-9_]+)", "\\1", "d,definition", NULL);
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* SwineParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "swn", NULL };
|
||||
parserDefinition* def = parserNew ("Swine");
|
||||
parserDefinition* const def = parserNew ("Makefile");
|
||||
def->patterns = patterns;
|
||||
def->extensions = extensions;
|
||||
def->initialize = installMakefileRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<pre>
|
||||
/***************************************************************************
|
||||
* swine.c
|
||||
* Regex callback-based parser for Swine definitions
|
||||
**************************************************************************/
|
||||
/* INCLUDE FILES */
|
||||
#include "general.h" /* always include first */
|
||||
|
||||
#include "parse.h" /* always include */
|
||||
#include "read.h" /* to define file fileReadLine() */
|
||||
|
||||
/* DATA DEFINITIONS */
|
||||
typedef enum eSwineKinds {
|
||||
K_DEFINE
|
||||
} swineKind;
|
||||
|
||||
static kindOption SwineKinds [] = {
|
||||
{ TRUE, 'd', "definition", "pig definition" }
|
||||
};
|
||||
|
||||
/* FUNCTION DEFINITIONS */
|
||||
|
||||
static void definition (const char *const line, const regexMatch *const matches,
|
||||
const unsigned int count)
|
||||
{
|
||||
if (count > 1) /* should always be true per regex */
|
||||
{
|
||||
vString *const name = vStringNew ();
|
||||
vStringNCopyS (name, line + matches [1].start, matches [1].length);
|
||||
makeSimpleTag (name, SwineKinds, K_DEFINE);
|
||||
}
|
||||
}
|
||||
|
||||
static void findSwineTags (void)
|
||||
{
|
||||
while (fileReadLine () != NULL)
|
||||
; /* don't need to do anything here since callback is sufficient */
|
||||
}
|
||||
|
||||
static void installSwine (const langType language)
|
||||
{
|
||||
addCallbackRegex (language, "^def[ \t]+([a-zA-Z0-9_]+)", NULL, definition);
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* SwineParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "swn", NULL };
|
||||
parserDefinition* def = parserNew ("Swine");
|
||||
def->kinds = SwineKinds;
|
||||
def->kindCount = KIND_COUNT (SwineKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findSwineTags;
|
||||
def->initialize = installSwine;
|
||||
return def;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<pre>
|
||||
/***************************************************************************
|
||||
* make.c
|
||||
* Regex-based parser for makefile macros
|
||||
**************************************************************************/
|
||||
/* INCLUDE FILES */
|
||||
#include "general.h" /* always include first */
|
||||
#include "parse.h" /* always include */
|
||||
|
||||
/* FUNCTION DEFINITIONS */
|
||||
|
||||
static void installMakefileRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language, "(^|[ \t])([A-Z0-9_]+)[ \t]*:?=", "\\2", "m,macro", "i");
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* MakefileParser (void)
|
||||
{
|
||||
static const char *const patterns [] = { "[Mm]akefile", NULL };
|
||||
static const char *const extensions [] = { "mak", NULL };
|
||||
parserDefinition* const def = parserNew ("Makefile");
|
||||
def->patterns = patterns;
|
||||
def->extensions = extensions;
|
||||
def->initialize = installMakefileRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,371 @@
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
* 1. Why do you call it "Exuberant Ctags"?
|
||||
* 2. Why doesn't my editor work with these tag files?
|
||||
* 3. What are these strange bits of text beginning with ;"?
|
||||
* 4. Why doesn't XEmacs' Speedbar module work with Exuberant Ctags?
|
||||
* 5. Why doesn't Xemacs correctly locate the tag in the source file?
|
||||
* 6. Why doesn't NEdit correctly locate the tag in the source file?
|
||||
* 7. Why can't I jump to "class::member"?
|
||||
* 8. How can I avoid having to specify my favorite option every time?
|
||||
* 9. Why do I end up on the wrong line when I jump to a tag?
|
||||
* 10. How do I jump to the tag I want instead of the wrong one by the
|
||||
same name?
|
||||
* 11. What is "Vim"?
|
||||
* 12. How can I locate all references to a specific function or variable?
|
||||
* 13. Why does appending tags to a tag file tag so long?
|
||||
* 14. How do I get regex support for Win32?
|
||||
* 15. How should I set up tag files for a multi-level directory hierarchy?
|
||||
|
||||
----------------------------------------------------------------------
|
||||
1. Why do you call it "Exuberant Ctags"?
|
||||
|
||||
Because one of the meanings of the word "exuberant" is:
|
||||
|
||||
exuberant : produced in extreme abundance : PLENTIFUL syn see PROFUSE
|
||||
|
||||
Compare the tag file produced by Exuberant Ctags with that produced by any
|
||||
other ctags and you will see how appropriate the name is.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
2. Why doesn't my editor work with these tag files?
|
||||
|
||||
3. What are these strange bits of text beginning with ;" which follow
|
||||
many of the lines in the tag file?
|
||||
|
||||
These are "extension flags". They are added in order to provide extra
|
||||
information about the tag that may be utilized by the editor in order to
|
||||
more intelligently handle tags. They are appended to the EX command part of
|
||||
the tag line in a manner that provides backwards compatibility with existing
|
||||
implementations of the Vi editor. The semicolon is an EX command separator
|
||||
and the double quote begins an EX comment. Thus, the extension flags appear
|
||||
as an EX comment and should be ignored by the editor when it processes the
|
||||
EX command.
|
||||
|
||||
Some non-vi editors, however, implement only the bare minimum of EX commands
|
||||
in order to process the search command or line number in the third field of
|
||||
the tag file. If you encounter this problem, use the option "--format=1" to
|
||||
generate a tag file without these extensions (remember that you can set the
|
||||
CTAGS environment variable to any default arguments you wish to supply). Then
|
||||
ask the supplier of your editor to implement handling of this feature of EX
|
||||
commands.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
4. Why doesn't XEmacs' Speedbar module work with Exuberant Ctags?
|
||||
|
||||
The default command line switches used by XEmacs for "etags" are not
|
||||
compatible with Exuberant Ctags options. By default, Exuberant Ctags installs
|
||||
a symbolic link, "etags", pointing to the ctags executable. When Exuberant
|
||||
Ctags is started with the name "etags", it produces Emacs-style tag files by
|
||||
default.
|
||||
|
||||
To fix this, add the following lines to your .emacs file, replacing the path
|
||||
to "etags" with the path where the symbolic link was installed.
|
||||
|
||||
(autoload 'speedbar "speedbar")
|
||||
(setq speedbar-fetch-etags-command "/usr/local/bin/etags"
|
||||
speedbar-fetch-etags-arguments '("-f" "-"))
|
||||
|
||||
----------------------------------------------------------------------
|
||||
5. Why doesn't Xemacs correctly locate the tag in the source file?
|
||||
|
||||
This has been observed with version 20.3. It seems that when Xemacs searches
|
||||
for a tag, it searches using the tag name instead of the search string located
|
||||
in the TAGS file. This is a bug in Xemacs and does not occur in the GNU
|
||||
version of Emacs.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
6. Why doesn't NEdit correctly locate the tag in the source file?
|
||||
|
||||
Versions of NEdit prior to 5.1 did not support the extended tag file format
|
||||
generated by Exuberant Ctags by default. Either upgrade to version 5.1 or
|
||||
specify the option "--format=1" when running ctags to output the old tag file
|
||||
format.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
7. Why can't I jump to "class::member"?
|
||||
|
||||
Because, by default, ctags only generates tags for the separate identifiers
|
||||
found in the source files. If you specify the --extra=+q option, then
|
||||
ctags will also generate a second, class-qualified tag for each class member
|
||||
(data and function/method) in the form class::member for C++, and in the form
|
||||
class.method for Eiffel and Java.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
8. How can I avoid having to specify my favorite option every time?
|
||||
|
||||
Either by setting the environment variable CTAGS to your custom
|
||||
options, or putting them into a .ctags file in your home directory.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
9. Why do I end up on the wrong line when I jump to a tag?
|
||||
|
||||
By default, ctags encodes the line number in the file where macro (#define)
|
||||
tags are found. This was done to remain compatible with the original UNIX
|
||||
version of ctags. If you change the file containing the tag without
|
||||
rebuilding the tag file, the location of tag in the tag file may no longer
|
||||
match the current location.
|
||||
|
||||
In order to avoid this problem, you can specify the option "--excmd=p",
|
||||
which causes ctags to use a search pattern to locate macro tags. I have
|
||||
never uncovered the reason why the original UNIX ctags used line numbers
|
||||
exclusively for macro tags, but have so far resisted changing the default
|
||||
behaviour of Exuberant Ctags to behave differently.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
10. How do I jump to the tag I want instead of the wrong one by the
|
||||
same name?
|
||||
|
||||
A tag file is simple a list of tag names and where to find them. If there
|
||||
are duplicate entries, you often end up going to the wrong one because the
|
||||
tag file is sorted and your editor locates the first one in the tag file.
|
||||
|
||||
Standard Vi provides no facilities to alter this behavior. However, Vim
|
||||
has some nice features to minimize this problem, primarly by examining all
|
||||
matches and choosing the best one under the circumstances. Vim also provides
|
||||
commands which allow for selection of the desired matching tag.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
11. What is "Vim"?
|
||||
|
||||
Vim is a vi-compatible editor available as source and compilable for any
|
||||
platform. Yeah, I know the first reaction is to shy away from this. But you
|
||||
will never regret getting it, and you will become greatly attached to its
|
||||
features, which you can learn gradually. I would be willing to say that it
|
||||
is the best vi-clone available within 4 light-years of Alpha Centauri. It
|
||||
works (nearly) exactly like standard vi, but provides some incredibly useful
|
||||
extensions (some of which I have participated in designing with the author).
|
||||
Most Linux distributions have adopted Vim as its standard vi.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
12. How can I locate all references to a specific function or variable?
|
||||
|
||||
There are several packages already available which provide this capability.
|
||||
Namely, these are: GLOBAL source code tag system, GNU id-utils, cscope,
|
||||
and cflow. As of this writing, they can be found in the following locations:
|
||||
|
||||
GLOBAL: http://www.gnu.org/software/global
|
||||
id-utils: http://www.gnu.org/software/idutils/idutils.html
|
||||
cscope: http://cscope.sourceforge.net
|
||||
cflow: ftp://www.ibiblio.org/pub/Linux/devel/lang/c
|
||||
|
||||
----------------------------------------------------------------------
|
||||
13. Why does appending tags to a tag file tag so long?
|
||||
|
||||
Sometimes, in an attempt to build a global tag file for all source files in
|
||||
a large source tree of many directories, someone will make an attempt to run
|
||||
ctags in append (-a) mode on every directory in the hierarchy. Each time
|
||||
ctags is invoked, its default behavior is to sort the tag file once the tags
|
||||
for that execution have been added. As the cumulative tag file grows, the sort
|
||||
time increases arithmetically.
|
||||
|
||||
The best way to avoid this problem (and the most efficient) is to make
|
||||
use of the --recurse (or -R) option of ctags by executing the following
|
||||
command in the root of the directory hierarchy (thus running ctags only once):
|
||||
|
||||
ctags -R
|
||||
|
||||
If you really insist on running ctags separately on each directory, you can
|
||||
avoid the sort pass each time by specifying the option "--sort=no". Once the
|
||||
tag file is completely built, use the sort command to manually sort the
|
||||
final tag file, or let the final invocation of ctags sort the file.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
14. How do I get regex support for Win32?
|
||||
|
||||
You need to download the GNU regex package for Win32 from the following
|
||||
location:
|
||||
|
||||
http://people.delphiforums.com/gjc/gnu_regex.html
|
||||
|
||||
Then point the makefile macro, REGEX_DIR, found in mk_mvc.mak and mk_bc5.mak,
|
||||
to the directory created by extracting this archive.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
15. How should I set up tag files for a multi-level directory hierarchy?
|
||||
|
||||
There are a few ways of approaching this:
|
||||
|
||||
1. A local tag file in each directory containing only the tags for source
|
||||
files in that directory.
|
||||
|
||||
2. One single big, global tag file present in the root directory of your
|
||||
hierarchy, containing all tags present in all source files in the
|
||||
hierarchy.
|
||||
|
||||
3. A local tag file in each directory containing only the tags for source
|
||||
files in that directory, in addition to one single global tag file
|
||||
present in the root directory of your hierarchy, containing all
|
||||
non-static tags present in all source files in the hierarchy.
|
||||
|
||||
4. A local tag file in each directory of the hierarchy, each one
|
||||
containing all tags present in source files in that directory and all
|
||||
non-static tags in every directory below it (note that this implies
|
||||
also having one big tag file in the root directory of the hierarchy).
|
||||
|
||||
Each of these approaches has its own set of advantages and disadvantages,
|
||||
depending upon your particular conditions. Which approach is deemed best
|
||||
depends upon the following factors:
|
||||
|
||||
A. The ability of your editor to use multiple tag files.
|
||||
|
||||
If your editor cannot make use of multiple tag files (original vi
|
||||
implementations could not), then one large tag file is the only way to
|
||||
go if you ever desire to jump to tags located in other directories. If
|
||||
you never need to jump to tags in another directory (i.e. the source
|
||||
in each directory is entirely self-contained), then a local tag file
|
||||
in each directory will fit your needs.
|
||||
|
||||
B. The time is takes for your editor to look up a tag in the tag file.
|
||||
|
||||
The significance of this factor depends upon the size of your source
|
||||
tree and on whether the source files are located on a local or remote
|
||||
file system. For source and tag files located on a local file system,
|
||||
looking up a tag is not as big a hit as one might first imagine, since
|
||||
vi implementations typically perform a binary search on a sorted tag
|
||||
file. This may or may not be true for the editor you use. For files
|
||||
located on a remote file system, reading a large file is an expensive
|
||||
operation.
|
||||
|
||||
C. Whether or not you expect the source code to change and the time it
|
||||
takes to rebuild a tag file to account for changes to the source code.
|
||||
|
||||
While Exuberant Ctags is particularly fast in scanning source code
|
||||
(around 1-2 MB/sec), a large project may still result in objectionable
|
||||
delays if one wishes to keep their tag file(s) up to date on a
|
||||
frequent basis, or if the files are located on a remote file system.
|
||||
|
||||
D. The presence of duplicate tags in the source code and the ability to
|
||||
handle them.
|
||||
|
||||
The impact of this factor is influenced by the following three issues:
|
||||
|
||||
1. How common are duplicate tags in your project?
|
||||
|
||||
2. Does your editor provide any facilities for dealing with duplicate
|
||||
tags?
|
||||
|
||||
While standard vi does not, many modern vi implementations, such
|
||||
as Vim have good facilities for selecting the desired match from
|
||||
the list of duplicates. If your editor does not support duplicate
|
||||
tags, then it will typically send you to only one of them, whether
|
||||
or not that is the one you wanted (and not even notifying you that
|
||||
there are other potential matches).
|
||||
|
||||
3. What is the significance of duplicate tags?
|
||||
|
||||
For example, if you have two tags of the same name from entirely
|
||||
isolated software components, jumping first to the match found
|
||||
in component B while working in component A may be entirely
|
||||
misleading, distracting or inconvenient (to keep having to choose
|
||||
which one if your editor provides you with a list of matches).
|
||||
However, if you have two tags of the same name for parallel builds
|
||||
(say two initialization routines for different hosts), you may
|
||||
always want to specify which one you want.
|
||||
|
||||
Of the approaches listed above, I tend to favor Approach 3. My editor of
|
||||
choice is Vim, which provides a rich set of features for handling multiple
|
||||
tag files, which partly influences my choice. If you are working with
|
||||
source files on a remote file system, then I would recommend either
|
||||
Approach 3 or Approach 4, depending upon the hit when reading the global
|
||||
tag file.
|
||||
|
||||
The advantages of Approach 3 are many (assuming that your editor has
|
||||
the ability to support both multiple tag files and duplicate tags). All
|
||||
lookups of tag located in the currect directory are fast and the local
|
||||
tag file can be quickly and easily regenerated in one second or less
|
||||
(I have even mapped a keystroke to do this easily). A lookup of a
|
||||
(necessarily non-static) tag found in another directory fails a lookup in
|
||||
the local tag file, but is found in the global tag file, which satisfies
|
||||
all cross-directory lookups. The global tag file can be automatically
|
||||
regenerated periodically with a cron job (and perhaps the local tag files
|
||||
also).
|
||||
|
||||
Now I give an example of how you would implement Approach 3. Means of
|
||||
implementing the other approaches can be performed in a similar manner.
|
||||
|
||||
Here is a visual representation of an example directory hierarchy:
|
||||
|
||||
project
|
||||
`-----misccomp
|
||||
| `...
|
||||
`-----sysint
|
||||
`-----client
|
||||
| `-----hdrs
|
||||
| `-----lib
|
||||
| `-----src
|
||||
| `-----test
|
||||
`-----common
|
||||
| `-----hdrs
|
||||
| `-----lib
|
||||
| `-----src
|
||||
| `-----test
|
||||
`-----server
|
||||
`-----hdrs
|
||||
`-----lib
|
||||
`-----src
|
||||
`-----test
|
||||
|
||||
Here is a recommended solution (conceptually) to build the tag files:
|
||||
|
||||
1. Within each of the leaf nodes (i.e. hdrs, lib, src, test) build a tag
|
||||
file using "ctags *.[ch]". This can be easily be done for the whole
|
||||
hierarchy by making a shell script, call it "dirtags", containing the
|
||||
following lines:
|
||||
|
||||
#!/bin/sh
|
||||
cd $1
|
||||
ctags *
|
||||
|
||||
Now execute the following command:
|
||||
|
||||
find * -type d -exec dirtags {} \;
|
||||
|
||||
These tag files are trivial (and extremely quick) to rebuild while
|
||||
making changes within a directory. The following Vim key mapping is
|
||||
quite useful to rebuild the tag file in the directory of the current
|
||||
source file:
|
||||
|
||||
:nmap ,t :!(cd %:p:h;ctags *.[ch])&<CR><CR>
|
||||
|
||||
2. Build the global tag file:
|
||||
|
||||
cd ~/project
|
||||
ctags --file-scope=no -R
|
||||
|
||||
thus constructing a tag file containing only non-static tags for all
|
||||
source files in all descendent directories.
|
||||
|
||||
3. Configure your editor to read the local tag file first, then consult
|
||||
the global tag file when not found in the local tag file. In Vim,
|
||||
this is done as follows:
|
||||
|
||||
:set tags=./tags,tags,~/project/tags
|
||||
|
||||
If you wish to implement Approach 4, you would need to replace the
|
||||
"dirtags" script of step 1 with the following:
|
||||
|
||||
#!/bin/sh
|
||||
cd $1
|
||||
ctags *
|
||||
# Now append the non-static tags from descendent directories
|
||||
find * -type d -prune -print | ctags -aR --file-scope=no -L-
|
||||
|
||||
And replace the configuration of step 3 with this:
|
||||
|
||||
:set tags=./tags,./../tags,./../../tags,./../../../tags,tags
|
||||
|
||||
As a caveat, it should be noted that step 2 builds a global tag file whose
|
||||
file names will be relative to the directory in which the global tag file
|
||||
is being built. This takes advantage of the Vim 'tagrelative' option,
|
||||
which causes the path to be interpreted a relative to the location of the
|
||||
tag file instead of the current directory. For standard vi, which always
|
||||
interprets the paths as relative to the current directory, we need to
|
||||
build the global tag file with absolute path names. This can be
|
||||
accomplished by replacing step 2 with the following:
|
||||
|
||||
cd ~/project
|
||||
ctags --file-scope=no -R `pwd`
|
||||
|
||||
--
|
||||
@@ -0,0 +1,215 @@
|
||||
Custom Installation
|
||||
===================
|
||||
|
||||
These installation instructions are for Unix or Unix-like platforms (or at
|
||||
least, those platforms which are able to run a Bourne shell script). If you
|
||||
are attempting to install Exuberant Ctags on some other platform, see the file
|
||||
INSTALL.oth.
|
||||
|
||||
If you are not familiar with using the configure scripts generated by GNU
|
||||
autoconf, read the "Basic Installation" section below; then return here.
|
||||
The configure script in this package supports the following custom options:
|
||||
|
||||
--disable-etags By default, "make install" will install one
|
||||
binary, "ctags", one man page, "ctags.1", and
|
||||
create links to these two files by the names
|
||||
"etags" and "etags.1". If you do not want to
|
||||
install the "etags" links, use this option.
|
||||
|
||||
--disable-extended-format Ctags now appends "extension flags" to the
|
||||
end of each tag entry in a manner which is
|
||||
backwards with original Vi implementation
|
||||
(they are placed into an EX comment). This
|
||||
can be disabled via use of the ctags --format
|
||||
option. This configure option changes the
|
||||
default behavior of ctags to disable use of
|
||||
these extension flags (i.e. use the original
|
||||
tag file format).
|
||||
|
||||
--disable-external-sort Use this option to force use of an internal
|
||||
sort algorithm. On UNIX-like systems, ctags
|
||||
uses the sort utility of the operating system
|
||||
by default because it is more memory efficient.
|
||||
|
||||
--enable-custom-config=FILE Defines a custom option configuration file to
|
||||
establish site-wide defaults. Ctags will read
|
||||
the following files at startup for options:
|
||||
/etc/ctags.conf, /usr/local/etc/ctags.conf,
|
||||
$HOME/.ctags, and .ctags. If you need a
|
||||
different file, set this option to the full
|
||||
path name of the file you want to be read, and
|
||||
it will be read immediately before reading
|
||||
$HOME/.ctags.
|
||||
|
||||
--enable-macro-patterns By default, line numbers are used in the tag
|
||||
file for #define objects, in order to remain
|
||||
compatible with the original UNIX ctags. This
|
||||
option will make the default use patterns.
|
||||
|
||||
--enable-maintainer-mode Creates a special GNU-specific version of the
|
||||
makefile which is used to maintain Exuberant
|
||||
Ctags.
|
||||
|
||||
--enable-tmpdir=DIR When the library function mkstemp() is
|
||||
available, this option allows specifying the
|
||||
default directory to use for temporary files
|
||||
generated by ctags. This default can be
|
||||
changed at run time by setting the environment
|
||||
variable TMPDIR.
|
||||
|
||||
If you wish to change the name of the installed files, edit the makefile
|
||||
produced by the configure script ("Makefile") before performing the "make
|
||||
install" step. There are two lines at the top of the file where the names of
|
||||
the installed files may be customized.
|
||||
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, a file
|
||||
`config.cache' that saves the results of its tests to speed up
|
||||
reconfiguring, and a file `config.log' containing compiler output
|
||||
(useful mainly for debugging `configure').
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If at some point `config.cache'
|
||||
contains results you don't want to keep, you may remove or edit it.
|
||||
|
||||
The file `configure.in' is used to create `configure' by a program
|
||||
called `autoconf'. You only need `configure.in' if you want to change
|
||||
it or regenerate `configure' using a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes awhile. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. You can give `configure'
|
||||
initial values for variables by setting them in the environment. Using
|
||||
a Bourne-compatible shell, you can do that on the command line like
|
||||
this:
|
||||
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
|
||||
|
||||
Or on systems that have the `env' program, you can do it like this:
|
||||
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not supports the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a time
|
||||
in the source code directory. After you have installed the package for
|
||||
one architecture, use `make distclean' before reconfiguring for another
|
||||
architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' will install the package's files in
|
||||
`/usr/local/bin', `/usr/local/man', etc. You can specify an
|
||||
installation prefix other than `/usr/local' by giving `configure' the
|
||||
option `--prefix=PATH'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
give `configure' the option `--exec-prefix=PATH', the package will use
|
||||
PATH as the prefix for installing programs and libraries.
|
||||
Documentation and other data files will still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=PATH' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Operation Controls
|
||||
==================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Use and save the results of the tests in FILE instead of
|
||||
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
|
||||
debugging `configure'.
|
||||
|
||||
`--help'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--version'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
If you are attempting to install Exuberant Ctags on a Unix-like platform
|
||||
(one that can at least run a Bourne shell script) see the file INSTALL.
|
||||
|
||||
Installation Notes
|
||||
==================
|
||||
|
||||
For non-Unix platforms, simple makefiles are provided:
|
||||
|
||||
descrip.mms For VMS using either DEC C or VAX C
|
||||
mk_bc3.mak For MSDOS using Borland C/C++ 3.x
|
||||
mk_bc5.mak For Win32 using Borland C++ 5.5
|
||||
mk_djg.mak For MSDOS using DJGPP Gnu GCC (better to follow Unix install)
|
||||
mk_manx.mak For Amiga using Aztec/Manx C 5.0
|
||||
mk_ming.mak For Win32 using Mingw32
|
||||
mk_mpw.mak For Macintosh using MPW
|
||||
mk_mvc.mak For Win32 using Microsoft Visual C++
|
||||
mk_os2.mak For OS/2 using GCC (EMX)
|
||||
mk_qdos.mak For QDOS using C68
|
||||
mk_riscos.mak For RISC OS using the GCC SDK <http://hard-mofo.dsvr.net>
|
||||
mk_sas.mak For Amiga using SAS/C
|
||||
|
||||
|
||||
Special Notes
|
||||
=============
|
||||
|
||||
Win32:
|
||||
------
|
||||
|
||||
Regex support on Win32 is available when compiled to use the Win32 port of
|
||||
the Gnu regex library, which can be obtained here:
|
||||
|
||||
http://people.delphiforums.com/gjc/gnu_regex.html
|
||||
|
||||
See the makefiles, mk_mvc.mak or mk_bc5.mak, to learn how to build with
|
||||
this library.
|
||||
|
||||
DJGPP:
|
||||
------
|
||||
|
||||
It is better to follow the standard Unix install on DJGPP, but this requires
|
||||
that you use BASH and requires a fairly complete installation of GJGPP
|
||||
packages. You can use mk_djg.mak if you can't run configure.
|
||||
|
||||
Macintosh with MPW:
|
||||
-------------------
|
||||
|
||||
To build Ctags on MPW you will have to have the Metrowerks compilers
|
||||
or you will have to edit the makefiles yourself.
|
||||
|
||||
Since the makefile for MPW is called mk_mpw.mak you will have to give
|
||||
the command: `Make -f mk_mpw.mak CTags` and then select the output
|
||||
and execute it. Alternatively you could rename this makefile to
|
||||
CTags.make and issue the command `Build CTags`.
|
||||
|
||||
If the build process goes wrong make sure the line endings for the
|
||||
makefile are set to MacOS (CR instead of LF).
|
||||
|
||||
This version of Ctags only accepts and emits unix style paths. This was
|
||||
done since it was easier to implement (few changes in main source files)
|
||||
and since I ported Ctags to MPW to use with Pepper which also works with
|
||||
unix style paths internally. Besides, since we're now moving to MacOS X
|
||||
anyway, Mac programmers are getting used to unix style paths anyway.
|
||||
|
||||
Example, suppose you have a project using PowerPlant which is located in
|
||||
'HD20:tools:PowerPlant:' and you want a tags file for all powerplant
|
||||
sources and your projects sources. Go to the root directory of your
|
||||
project and type:
|
||||
|
||||
CTags -R . '/HD20/tools/PowerPlant/'
|
||||
|
||||
The '.' in this command means current directory. You will also have to
|
||||
put quotes around the full unix style path since / has a special meaning
|
||||
in MPW.
|
||||
|
||||
RISC OS:
|
||||
--------
|
||||
|
||||
Regex support on RISC OS is available when compiled to use the RISC OS port
|
||||
of the GNU regex library, which can be obtained from:
|
||||
|
||||
<http://www.sbellon.de/software.html>
|
||||
|
||||
Using 'Set RexEx$Path <Obey$Dir>.' in the regex-0/12 directory will ensure
|
||||
it can be found.
|
||||
@@ -0,0 +1,54 @@
|
||||
SubDir HAIKU_TOP src bin ctags ;
|
||||
|
||||
SubDirCcFlags -DHAVE_CONFIG_H ;
|
||||
|
||||
SUBDIRYACCFLAGS += d ;
|
||||
|
||||
SubDirSysHdrs [ FDirName $(SUBDIR) ] ;
|
||||
|
||||
BinCommand ctags :
|
||||
args.c
|
||||
asm.c
|
||||
asp.c
|
||||
awk.c
|
||||
beta.c
|
||||
c.c
|
||||
cobol.c
|
||||
eiffel.c
|
||||
entry.c
|
||||
erlang.c
|
||||
fortran.c
|
||||
get.c
|
||||
html.c
|
||||
jscript.c
|
||||
keyword.c
|
||||
lisp.c
|
||||
lregex.c
|
||||
lua.c
|
||||
main.c
|
||||
make.c
|
||||
options.c
|
||||
parse.c
|
||||
pascal.c
|
||||
perl.c
|
||||
php.c
|
||||
python.c
|
||||
read.c
|
||||
rexx.c
|
||||
routines.c
|
||||
ruby.c
|
||||
scheme.c
|
||||
sh.c
|
||||
slang.c
|
||||
sml.c
|
||||
sort.c
|
||||
sql.c
|
||||
strlist.c
|
||||
tcl.c
|
||||
verilog.c
|
||||
vim.c
|
||||
yacc.c
|
||||
vstring.c
|
||||
:
|
||||
: ctags.rdef ;
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
# $Id: Makefile.in,v 1.8 2003/07/27 22:09:58 darren Exp $
|
||||
#
|
||||
# Makefile for UNIX-like platforms.
|
||||
#
|
||||
|
||||
# These are the names of the installed programs, in case you wish to change
|
||||
# them.
|
||||
#
|
||||
CTAGS_PROG = ctags
|
||||
ETAGS_PROG = etags
|
||||
|
||||
# Set this to the path to your shell (must run Bourne shell commands).
|
||||
#
|
||||
SHELL = /bin/sh
|
||||
|
||||
# GNU Autoconf variables. These are set by the "configure" script when it
|
||||
# runs.
|
||||
#
|
||||
exec_prefix = @exec_prefix@
|
||||
prefix = @prefix@
|
||||
bindir = @bindir@
|
||||
srcdir = @srcdir@
|
||||
libdir = @libdir@
|
||||
incdir = @includedir@
|
||||
mandir = @mandir@
|
||||
SLINK = @LN_S@
|
||||
STRIP = @STRIP@
|
||||
CC = @CC@
|
||||
DEFS = @DEFS@
|
||||
CFLAGS = @CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
EXEEXT = @EXEEXT@
|
||||
OBJEXT = @OBJEXT@
|
||||
|
||||
# If you cannot run the "configure" script to set the variables above, then
|
||||
# uncomment the defines below and customize them for your environment. If
|
||||
# your system does not support symbolic (soft) links, then remove the -s
|
||||
# from SLINK.
|
||||
#
|
||||
#srcdir = .
|
||||
#bindir = /usr/local/bin
|
||||
#mandir = /usr/local/man
|
||||
#SLINK = ln -s
|
||||
#STRIP = strip
|
||||
#CC = cc
|
||||
#DEFS = -DHAVE_CONFIG_H
|
||||
#CFLAGS = -O
|
||||
#LDFLAGS=
|
||||
|
||||
include $(srcdir)/source.mak
|
||||
|
||||
#
|
||||
#--- You should not need to modify anything below this line. ---#
|
||||
#
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .$(OBJEXT)
|
||||
|
||||
VPATH = $(srcdir)
|
||||
|
||||
INSTALL = cp
|
||||
INSTALL_PROG = $(INSTALL)
|
||||
INSTALL_DATA = $(INSTALL)
|
||||
|
||||
READ_LIB = readtags.$(OBJEXT)
|
||||
READ_INC = readtags.h
|
||||
|
||||
MANPAGE = ctags.1
|
||||
|
||||
AUTO_GEN = configure config.h.in
|
||||
CONFIG_GEN = config.cache config.log config.status config.run config.h Makefile
|
||||
|
||||
#
|
||||
# names for installed man pages
|
||||
#
|
||||
manext = 1
|
||||
man1dir = $(mandir)/man1
|
||||
CMAN = $(CTAGS_PROG).$(manext)
|
||||
EMAN = $(ETAGS_PROG).$(manext)
|
||||
|
||||
#
|
||||
# destinations for installed files
|
||||
#
|
||||
CTAGS_EXEC = $(CTAGS_PROG)$(EXEEXT)
|
||||
ETAGS_EXEC = $(ETAGS_PROG)$(EXEEXT)
|
||||
DEST_CTAGS = $(bindir)/$(CTAGS_EXEC)
|
||||
DEST_ETAGS = $(bindir)/$(ETAGS_EXEC)
|
||||
DEST_READ_LIB = $(libdir)/$(READ_LIB)
|
||||
DEST_READ_INC = $(incdir)/$(READ_INC)
|
||||
DEST_CMAN = $(man1dir)/$(CMAN)
|
||||
DEST_EMAN = $(man1dir)/$(EMAN)
|
||||
|
||||
#
|
||||
# primary rules
|
||||
#
|
||||
all: $(CTAGS_EXEC) $(READ_LIB)
|
||||
|
||||
$(CTAGS_EXEC): $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS)
|
||||
|
||||
dctags$(EXEEXT): debug.c $(SOURCES) $(HEADERS)
|
||||
$(CC) -I. -I$(srcdir) $(DEFS) -DDEBUG -g $(LDFLAGS) -o $@ debug.c $(SOURCES)
|
||||
|
||||
readtags$(EXEEXT): readtags.c readtags.h
|
||||
$(CC) -DREADTAGS_MAIN -I. -I$(srcdir) $(DEFS) $(CFLAGS) $(LDFLAGS) -o $@ readtags.c
|
||||
|
||||
ETYPEREF_OBJS = etyperef.o keyword.o routines.o strlist.o vstring.o
|
||||
etyperef$(EXEEXT): $(ETYPEREF_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(ETYPEREF_OBJS)
|
||||
|
||||
etyperef.o: eiffel.c
|
||||
$(CC) -DTYPE_REFERENCE_TOOL -I. -I$(srcdir) $(DEFS) $(CFLAGS) -o $@ -c eiffel.c
|
||||
|
||||
$(OBJECTS): $(HEADERS) config.h
|
||||
|
||||
#
|
||||
# generic install rules
|
||||
#
|
||||
install: @install_targets@
|
||||
|
||||
install-strip: install
|
||||
|
||||
install-ctags: install-cbin install-cman
|
||||
install-etags: install-ebin install-eman
|
||||
|
||||
$(bindir) $(man1dir) $(libdir) $(incdir):
|
||||
$(srcdir)/mkinstalldirs $@
|
||||
|
||||
FORCE:
|
||||
|
||||
#
|
||||
# install the executables
|
||||
#
|
||||
install-bin: install-cbin install-ebin install-lib
|
||||
install-cbin: $(DEST_CTAGS)
|
||||
install-ebin: $(DEST_ETAGS)
|
||||
install-lib: $(DEST_READ_LIB) $(DEST_READ_INC)
|
||||
|
||||
$(DEST_CTAGS): $(CTAGS_EXEC) $(bindir) FORCE
|
||||
$(INSTALL_PROG) $(CTAGS_EXEC) $@ && chmod 755 $@
|
||||
|
||||
$(DEST_ETAGS):
|
||||
- if [ -x $(DEST_CTAGS) ]; then \
|
||||
cd $(bindir) && $(SLINK) $(CTAGS_EXEC) $(ETAGS_EXEC); \
|
||||
fi
|
||||
|
||||
#
|
||||
# install the man pages
|
||||
#
|
||||
install-man: install-cman install-eman
|
||||
install-cman: $(DEST_CMAN)
|
||||
install-eman: $(DEST_EMAN)
|
||||
|
||||
$(DEST_CMAN): $(man1dir) $(MANPAGE) FORCE
|
||||
- $(INSTALL_DATA) $(srcdir)/$(MANPAGE) $@ && chmod 644 $@
|
||||
|
||||
$(DEST_EMAN):
|
||||
- if [ -f $(DEST_CMAN) ]; then \
|
||||
cd $(man1dir) && $(SLINK) $(CMAN) $(EMAN); \
|
||||
fi
|
||||
|
||||
#
|
||||
# install the library
|
||||
#
|
||||
$(DEST_READ_LIB): $(READ_LIB) $(libdir) FORCE
|
||||
$(INSTALL_PROG) $(READ_LIB) $@ && chmod 644 $@
|
||||
|
||||
$(DEST_READ_INC): $(READ_INC) $(incdir) FORCE
|
||||
$(INSTALL_PROG) $(READ_INC) $@ && chmod 644 $@
|
||||
|
||||
|
||||
#
|
||||
# rules for uninstalling
|
||||
#
|
||||
uninstall: uninstall-bin uninstall-lib uninstall-man
|
||||
|
||||
uninstall-bin:
|
||||
- rm -f $(DEST_CTAGS) $(DEST_ETAGS)
|
||||
|
||||
uninstall-lib:
|
||||
- rm -f $(DEST_READ_LIB) $(DEST_READ_INC)
|
||||
|
||||
uninstall-man:
|
||||
- rm -f $(DEST_CMAN) $(DEST_EMAN)
|
||||
|
||||
uninstall-ctags:
|
||||
- rm -f $(DEST_CTAGS) $(DEST_CMAN)
|
||||
|
||||
uninstall-etags:
|
||||
- rm -f $(DEST_ETAGS) $(DEST_EMAN)
|
||||
|
||||
#
|
||||
# miscellaneous rules
|
||||
#
|
||||
tags: $(CTAGS_EXEC)
|
||||
./$(CTAGS_EXEC) $(srcdir)/*
|
||||
|
||||
TAGS: $(CTAGS_EXEC)
|
||||
./$(CTAGS_EXEC) -e $(srcdir)/*
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(CTAGS_EXEC) tags TAGS $(READ_LIB)
|
||||
rm -f dctags$(EXEEXT) readtags$(EXEEXT)
|
||||
rm -f etyperef$(EXEEXT) etyperef.$(OBJEXT)
|
||||
|
||||
mostlyclean: clean
|
||||
|
||||
distclean: clean
|
||||
rm -f $(CONFIG_GEN)
|
||||
|
||||
maintainerclean: distclean
|
||||
rm -f $(AUTO_GEN)
|
||||
|
||||
#
|
||||
# implicit rules
|
||||
#
|
||||
.c.$(OBJEXT):
|
||||
$(CC) -I. -I$(srcdir) $(DEFS) $(CFLAGS) -c $<
|
||||
|
||||
# vi:set tabstop=8:
|
||||
@@ -0,0 +1,796 @@
|
||||
Current Version: 5.6
|
||||
|
||||
ctags-5.6 (Mon May 29 2006)
|
||||
* Reformatted code for independence of tab stop setting.
|
||||
* Changed default configuration to disable installation of etags links.
|
||||
* Changed --langmap to first unmap each supplied extension from other languages.
|
||||
* Added support for ASP constants [ASP, Patch #961842].
|
||||
* Added support for GNU make extensions [Make].
|
||||
* Added .mk as extension recognized as a make language file [Make].
|
||||
* Added missing help for list-maps options [Bug #1201826].
|
||||
* Added new extension field "typeref" [thanks to Bram Moolenaar].
|
||||
* Extended functionality of Ruby parser with patch from Elliot Hughes [Ruby].
|
||||
* Fixed creation of TAGS file with etags-include but no files [Bug #941233].
|
||||
* Fixed problem reading last line of list file (-L) without final newline.
|
||||
* Fixed infinite loop that could occur on files without final newline [C, Java].
|
||||
* Fixed incorrect tag for first field of table [SQL].
|
||||
* Fixed missing tags for functions beginning with underscore [Sh].
|
||||
* Fixed missing tags for functions with variable arg list [C, Bug #1201689].
|
||||
* Fixed parsing problem with parentheses in argument list [C, Bug #1085585].
|
||||
* Fixed problem in preprocessor directive handling [C, Bug #1086609].
|
||||
|
||||
ctags-5.5.4 (Thu Mar 25 2004)
|
||||
* Fixed broken -R option.
|
||||
|
||||
ctags-5.5.3 (Sun Mar 14 2004)
|
||||
* Removed forgotten debug statement [Bug #811704].
|
||||
* Added support for Perl labels.
|
||||
* Added support for Perl "use constant" [Perl, Patch #853704, Feature Request
|
||||
#710017].
|
||||
* Added support for package qualification of tags, removing useless "package"
|
||||
tag kind [Perl, Feature Request #448887].
|
||||
* Added support for "and" keyword [SML, Bug #816636].
|
||||
* Added support for variables [PHP].
|
||||
* Fixed problem destroying tag file with certain info options [Bug #845502].
|
||||
* Fixed portability problem [DJGPP].
|
||||
* Fixed problem of double characters in signature field [C, Bug #852368].
|
||||
* Fixed problem manifested by errant preprocessor conditionals [Bug #839162].
|
||||
* Fixed incorrect line address in tag file for SQL tags [SQL, Bug #823000].
|
||||
* Fixed incorrect recognition of POD paragraph [Perl, Bug #842077].
|
||||
* Fixed spurious tags for for C++ member templtates [C++, Bug #849591].
|
||||
* Fixed missing tags related to template specializations [C++, Bug #872494].
|
||||
* Fixed spurious local tags for statements following labels [C].
|
||||
* Fixed missing tags for certain scoped functions [Vim].
|
||||
* Fixed infinite loop in Fortran parser.
|
||||
* Fixed missing tags for certain initializers [Fortran, Bug #877956].
|
||||
* Fixed problem with comment line after continuation character [Fortran,
|
||||
Bug #858165].
|
||||
|
||||
ctags-5.5.2 (Wed Sep 17 2003)
|
||||
* Added tags for local variables for C-based languages [C/C++/C#/Java/Vera,
|
||||
Feature Request #449503].
|
||||
* Fixed compilation problem due to type change made to accomodate change of
|
||||
return type of _findfirst() in VisualStudio.Net [Win32, Bug #775789].
|
||||
* Fixed problems with certain bit fields.
|
||||
|
||||
ctags-5.5.1 (Wed Jul 30 2003)
|
||||
* Changed supported tag kinds for Verilog parser during overhaul.
|
||||
* Restored exit of program after --help, --license, and --version options [Bug
|
||||
#717311, #751240].
|
||||
* Removed inclusion of general.h (GPL) from readtags.c (public domain).
|
||||
* Added support for tags for labels [PL/SQL].
|
||||
* Added support for tags for constant definitions [PHP].
|
||||
* Fixed redundant parsing of configuration file [Windows, Bug #768814].
|
||||
* Fixed missing tags for definitions spanning lines [Verilog, Bug #762027].
|
||||
* Fixed compilation error for uncommon hosts.
|
||||
* Fixed missing tags for Korn shell specific function definitions. [Sh,
|
||||
Bug #769184]
|
||||
* Fixed missing tags when semicolon separator appears [Fortran, Bug #734933].
|
||||
* Fixed missing tags when endsubroutine keyword appears [Fortran, Bug #726712].
|
||||
* Fixed problem with fixed-form line continuation following comment [Fortran,
|
||||
Bug #726875].
|
||||
* Fixed missing tags for nested blocks [PL/SQL, Bug #722501].
|
||||
* Fixed missing tags for function typedefs [C].
|
||||
* Fixed inability to map empty extension when path contained dot [Bug #742689].
|
||||
|
||||
ctags-5.5 (Tue Apr 1 2003)
|
||||
* Changed kind indicator for methods from 'f' to 'm' [Tcl].
|
||||
* Changed tags within interfaces to be disabled by default (like prototypes in
|
||||
C/C++) [Fortran].
|
||||
* Removed explicit descriptions of individual --<LANG>-types options from
|
||||
--help output. See new --list-languages and --list-kinds options.
|
||||
* Removed explicit list of supported languages and supported tag kinds and
|
||||
mapping patterns from man page. See new --list-languages, --list-kinds, and
|
||||
--list-maps options.
|
||||
* Renamed --<LANG>-types option to --<LANG>-kinds (still accepts old name).
|
||||
* Added --list-kinds option.
|
||||
* Added --list-maps option.
|
||||
* Added --list-languages option.
|
||||
* Added support for dimensioned variables, contributed by Simon Bohlin [ASP].
|
||||
* Added support for C# language.
|
||||
* Added support for Erlang language, contributed by Brent Fulgham.
|
||||
* Added support for HTML language files.
|
||||
* Added support for JavaScript language files.
|
||||
* Added support for SML (Standard ML) language, contributed by Venkatesh Prasad.
|
||||
* Added mapping for .plx to Perl.
|
||||
* Added tags for autocommand groups [Vim, Patch #664685].
|
||||
* Added support for numerous language extensions [Fortran].
|
||||
* Added '$', 'D', and 'd' in column 1 as comment characters [Fortran].
|
||||
* Added special handling of --options=NONE to disable automatic reading of
|
||||
options from configuration files or environment.
|
||||
* Added check for case-insensitive filenames to configure.
|
||||
* Fixed problem with lower case <SID> tag [Vim, Bug #657327].
|
||||
* Fixed problem recognizing indented code [Vim, Patch #664685].
|
||||
* Fixed problem with infinite loop in certain comments [PL/SQL, Bug #629115].
|
||||
* Fixed problem of incorrect extension field [C, Bug #639639].
|
||||
* Fixed problem of empty scoping extension field [C, Bug #639644].
|
||||
* Fixed missing tags for functions split across lines [PHP, Bug #681824].
|
||||
* Fixed missing tags for nested subprograms using 'contains' [Fortran,
|
||||
Bug #670433].
|
||||
* Fixed missing tags when variable has same name as keyword [Fortran].
|
||||
* Fixed spurious tag when an array-spec occurs within an entity-decl [Fortran].
|
||||
* Fixed mishandling of multiline raw strings [Python, Bug #699171].
|
||||
* Fixed missing scope extension field on namespaces [C++, C#, Bug #665086].
|
||||
* Fixed several bugs causing missed tags [Fortran].
|
||||
* Fixed problem with --langmap option preventing clearing of map [Bug #688442].
|
||||
* Fixed recognition of Unicode-8 characters [Java].
|
||||
* Fixed man page errors and omissions.
|
||||
* Fixed bug in readFieldValue() in readtags library.
|
||||
* Fixed bug in option parsing in readtags command-line program.
|
||||
* Fixed portability problems with DJGPP [Bug #692569].
|
||||
* Fixed portability problems with Cygwin.
|
||||
|
||||
ctags-5.4 (Thu Oct 17 2002)
|
||||
* Improved ability for tagsOpen() in readtags library to report failure to
|
||||
open tag file, adding new fields to tagFileInfo structure.
|
||||
* Improved Cobol support to include data, files, groups, and sections [Cobol].
|
||||
* Added '$' a valid character for C identifier [VMS].
|
||||
* Added support for recording routine argument declarations for C-like
|
||||
languages. See the --fields option and man page section TAG FILE FORMAT for
|
||||
more information [C, C++, Java].
|
||||
* Added class and method support to TCL parser [TCL].
|
||||
* Added support for PL/SQL language.
|
||||
* Added support for Vera language, inspired by Dave Eggum [Vera].
|
||||
* Fixed problem terminating Perl POD block [Perl, Bug #612621].
|
||||
* Fixed problem re whitespace preceding subprogram name [Pascal, Bug #612019].
|
||||
* Fixed problem with leading spaces before instruction [TCL, Bug #615928].
|
||||
* Fixed problem with double precision functions [Fortran, Bug #620288].
|
||||
* Fixed inverted test causing TMPDIR to be used for temporary files when
|
||||
ctags is setuid instead of when not setuid [Bug #623713].
|
||||
|
||||
ctags-5.3.1 (Thu Sep 12 2002)
|
||||
* Renamed tagsSetSorted() to tagsSetSortType() and "sorted" member of
|
||||
tagFileInfo structure of readtags library to "sort".
|
||||
* Added new function, tagsFirst() to readtags library.
|
||||
* Fixed incorrect tag kinds [Verilog].
|
||||
* Fixed null tags for unnamed BLOCK DATA statements [Fortran].
|
||||
* Fixed missing tags for function preceded by "<SID>" [Vim].
|
||||
* Fixed missing tags for equate statements not in column 1 [Asm, Bug #538629].
|
||||
* Fixed Ruby parser (why didn't the compiler report my screw-up?) [Ruby].
|
||||
|
||||
ctags-5.3 (Wed Jul 17 2002)
|
||||
* Allowed --etags-include option without input files.
|
||||
* Changed Asm parser to back to C-based parser to remove redundant tags,
|
||||
and extending its support for more variants [Asm].
|
||||
* Changed to using _tempnam() to create temporary files on Windows, allowing
|
||||
"TMP" environment variable to set temporary directory.
|
||||
* Changed the -x output to match that of traditional ctags when the --format=1
|
||||
option is supplied. The new format was also changed slightly to conform more
|
||||
closely to the original format, with the addition of the extra tag type field.
|
||||
* Added support for Verilog language, submitted by Nam SungHyun.
|
||||
* Added support for RISC OS platform, contributed by Andrew Wingate.
|
||||
* Added support for "#pragma weak", generating macro tags for weak symbols [C].
|
||||
* Added support for mixins and class methods to Ruby parser, submitted by
|
||||
Matthias Veit [Ruby].
|
||||
* Added support to ctags and readtags library for case-folded sorting of tag
|
||||
files, submitted by Flemming Madsen.
|
||||
* Added identification of class methods [Python].
|
||||
* Fixed portability problems [Bugs #541997, #571240].
|
||||
* Fixed bug in configure script [Solaris, Bug #542966].
|
||||
* Fixed invalid package name tags [Perl, Bug #535068].
|
||||
* Fixed failure to output relative paths into etags TAGS files on Win32
|
||||
[Bug #568365].
|
||||
* Fixed incorrect line address in cases of line continuation [Fortran].
|
||||
* Fixed missing tags for certain cases of invalid syntax [C].
|
||||
* Fixed missing tags in Fortran with HPF extensions [Fortran, Bug #565813].
|
||||
* Fixed spurious tag for clients portion of feature clause when following
|
||||
an empty feature clause [Eiffel].
|
||||
|
||||
ctags-5.2.3 (Sun Feb 24 2002)
|
||||
* Fixed portability problem in makefile [Solaris, FreeBSD].
|
||||
* Fixed infinite loop for certain cases of invalid syntax [Eiffel].
|
||||
* Changed Asm parser to regex, extending its support for more variants [Asm].
|
||||
|
||||
ctags-5.2.2 (Sat Feb 16 2002)
|
||||
* Fixed spurious tags following empty feature clause [Eiffel].
|
||||
* Fixed missing tags for classes specifying generic creation routine [Eiffel].
|
||||
* Fixed missing tags when label not followed by white space [YACC].
|
||||
* Fixed for portability [Solaris, MacOS X].
|
||||
* Added support for type reference tool [Eiffel].
|
||||
|
||||
ctags-5.2.1 (Sun Jan 20 2002)
|
||||
* Portability fixes [Mingw32].
|
||||
* Added "RCS" and "CVS" to list of directories excluded by default.
|
||||
* Fixed missing tags for function pointers declared const or volatile
|
||||
[C, Bug #503764].
|
||||
|
||||
ctags-5.2 (Sun Dec 23 2001)
|
||||
* Portability fixes [HP-UX, Solaris, VMS, OS/2].
|
||||
* Made code compilable by a C++ compiler.
|
||||
* Changed reading of option files to ignore blank lines.
|
||||
* Changed and enhanced interface to readtags library (see readtags.h).
|
||||
* Changed from using addLanguageRegex() to addTagRegex() in regex-based
|
||||
parsers.
|
||||
* Added support for Lua language, submitted by Max Ischenko.
|
||||
* Added instructions to man page on using tags with NEdit.
|
||||
* Added setargv.obj to link for wildcard expansion [MSVC].
|
||||
* Added capability to have regex invoke a callback in a regex parser.
|
||||
* Fixed regex tag problem which left newlines in back-references.
|
||||
* Fixed missing class-qualified tags [Eiffel].
|
||||
* Fixed spurious tags for entries in final indexing clause [Eiffel].
|
||||
* Fixed problem with invalid filenames in preprocessor line directives.
|
||||
* Fixed bug parsing scoped variables (e.g. "b:variable") [Vim, Bug #487608].
|
||||
* Fixed problem compiling readtags.c on some hosts.
|
||||
* Fixed memory overwrite problem in readtags library.
|
||||
|
||||
ctags-5.1 (Tue Nov 06 2001)
|
||||
* Changed name of option configuration files for MSDOS, MSWindows, and OS/2.
|
||||
* Changed regex support to enforce REG_NEWLINE. This fixes problem where the
|
||||
newline character was explicity being matched by user patterns [Bug #431477].
|
||||
* Added new public domain library for reading tag files (see readtags.h).
|
||||
* Added support for variables and namespaces, provided by Jay Glanville [Vim].
|
||||
* Added report of non-options in option configuration files and CTAGS
|
||||
environment variable.
|
||||
* Added support for YACC language, submitted by Nick Hibma [YACC].
|
||||
* Added support for Perl packages, submitted by Nick Hibma [Perl].
|
||||
* Added '$' as valid identifier character for DEC C compiler [VMS, Bug #425147].
|
||||
* Added compilation date and time to --version output.
|
||||
* Added configure check for HP-UX to determine if ANSI options needed [HP-UX].
|
||||
* Removed tags for forward class/struct declarations [C/C++, Bug #432563].
|
||||
* Eliminated ;" separator from end of tag line when no extension fields are
|
||||
present.
|
||||
* Fixed segmentation violation for some Lisp files [Lisp].
|
||||
* Fixed segmentation violation occurring when file referenced in #line
|
||||
directive was from an unknown language.
|
||||
* Fixed loss of sync when parsing bit fields named with C++ reserved word [C].
|
||||
* Fixed compilation problem on gcc-2.7.2.
|
||||
* Fixed problem parsing verbatim strings [Eiffel].
|
||||
* Fixed problem with PHP references [PHP].
|
||||
* Fixed handling of Perl __DATA__ sections [Perl].
|
||||
* Fixed problem resulting from white space in tag name due to regex name
|
||||
specifier.
|
||||
* Fixed double reading of $HOME/.ctags when current directory is $HOME.
|
||||
* Fixed problem reading option configuration files using CR-LF newlines.
|
||||
* Fixed problem preventing output control over tag kinds of regex patterns
|
||||
[Bug #429869]
|
||||
* Fixed incorrect parsing of Vim functions with ':' modifiers [Bug #466517].
|
||||
|
||||
ctags-5.0.1 (Sun Apr 15 2001)
|
||||
* Fixed problem checking recursive links [SunOS 4.x].
|
||||
* Improved security on hosts where mkstemp() is not available.
|
||||
|
||||
ctags-5.0 (Sun Mar 18 2001)
|
||||
* Restructured code to simplify support for new language parsers.
|
||||
* Changed source code to use ANSI-style function definitions.
|
||||
* Changed scope-qualified tag entries to omit enumeration name [C/C++].
|
||||
* Changed reading of files supplied to -I option to read one token per line.
|
||||
* Changed reading of option files to read one argument per line.
|
||||
* Changed default extension fields, now controlled by new option --fields.
|
||||
* Changed detection of etags invocation to accept any name containing "etags".
|
||||
* Removed -p option, which only caused confusion and is rendered obsolete by
|
||||
the change immediately above.
|
||||
* Removed 'A' flag to the --c-types, --eiffel-types, and --java-types
|
||||
options, replacing its functionality with the new --fields option.
|
||||
* Removed 'C' flag to the --c-types, --eiffel-types, and --java-types
|
||||
options, replacing its functionality with the new --extra option.
|
||||
* Deprecated -i option, which was long ago replaced with the --c-types option.
|
||||
* Deprecated --file-tags option, now incorporated into new --extra option.
|
||||
* Deprecated --kind-long option, now incorporated into new --fields option.
|
||||
* Renamed --lang[uage] option to --language-force.
|
||||
* Renamed makefiles for non-Unix platforms.
|
||||
* Improved parsing of assembly language files [Asm].
|
||||
* Improved parsing of Fortran language files, adding new tag kinds [Fortran].
|
||||
* Added documentation explaining how to extend ctags with new parsers.
|
||||
* Added support for regular expressions, using either Posix or Gnu interface.
|
||||
* Added support for mapping file names to languages using shell patterns.
|
||||
* Added support for ASP scripts, submitted by Patrick Dehne [ASP].
|
||||
* Added support for Makefiles [Make].
|
||||
* Added support for Pascal language [Pascal].
|
||||
* Added support for PHP scripts, submitted by Jesus Castagnetto [PHP].
|
||||
* Added support for REXX language [REXX], based on submission by Alexaner Mai.
|
||||
* Added support for Ruby, submitted by Thaddeus Covert [Ruby].
|
||||
* Added support for S-Lang, submitted by Francesc Rocher [SLang].
|
||||
* Added support for Macintosh platform using MPW (by Maarten Hekkelman).
|
||||
* Added .tk as recognized extension [Tcl].
|
||||
* Added .cp and .hp as C++ extensions [C++].
|
||||
* Added .zsh as shell script extension [Sh].
|
||||
* Added support for trigraphs for C-based languages [C/C++].
|
||||
* Added language recognition for shell scripts using "#!/usr/bin/env command".
|
||||
* Added check for recursive directory links.
|
||||
* Added support for "[" form of verbatim strings [Eiffel].
|
||||
* Added --exclude option to exclude directories while recursing.
|
||||
* Added --fields option to specify extension fields to include in output.
|
||||
* Added --extra option to allow control over extra tags.
|
||||
* Added --regex-<LANG> option to define language-specific regular expressions.
|
||||
* Added --<LANG>-types options for all supported languages.
|
||||
* Added --langdef option to define new languages to be parsed with regex.
|
||||
* Added --languages option to restrict set of languages scanned for tags.
|
||||
* Added --tag-relative option to make file paths recorded in tag file relative
|
||||
to location of tag file itself instead of the current working directory when
|
||||
file arguments are specified using relative paths.
|
||||
* Added restriction of permissions of created temporary files when mkstemp()
|
||||
is not available for security.
|
||||
* Reimplemented line directive handling to work for all languages.
|
||||
* Fixed tag generation for packages [Java].
|
||||
* Fixed Lisp parser [Lisp].
|
||||
* Fixed Mingw32 port [Win32].
|
||||
* Fixed bug in procedure name parsing [Tcl].
|
||||
* Fixed bug resulting in wrong column being checked for paragraphs [Cobol].
|
||||
* Fixed bug in language dispatch for executable "#!" scripts [Unix].
|
||||
* Fixed bugs resulting in incorrect scope entries in tag file [C++/Java].
|
||||
* Fixed warning caused by reinstallation of etags link [Unix].
|
||||
* Fixed destruction of existing tag file when no files supplied on invocation.
|
||||
* Fixed problem in Makefile.in which prevented configuring and building in
|
||||
non-source directory. Also changed Makefile.in to generate and use correct
|
||||
object and executable file extensions when run on Win32.
|
||||
|
||||
ctags-4.0.3 (Sun Jul 16 2000)
|
||||
* Fixed compiler warnings [Amiga].
|
||||
* Fixed problem in configure.in causing struct stat st_ino member test to fail.
|
||||
* Fixed problem with TAGS entries for files using DOS-style (CR-LF) new lines.
|
||||
* Improved algorithm for locating Perl functions and skipping pods.
|
||||
* Improved algorithm for locating shell functions [Sh].
|
||||
* Renamed Makefile.amiga to Makefile.manx [Amiga].
|
||||
* Added Makefile.sas for SAS C compiler [Amiga].
|
||||
* Updated Makefile.qdos [QDOS].
|
||||
* Improved support for DECC compiler [VAX].
|
||||
|
||||
ctags-4.0.2 (Mon Jul 10 2000)
|
||||
* Now silently ignore -w option for backwards compatibility with SVR4 ctags.
|
||||
* Fixed bug resulting in no extension flags when using --kind-long option.
|
||||
|
||||
ctags-4.0.1 (Wed Jun 28 2000)
|
||||
* Fixed segmentation violation when using --file-tags.
|
||||
|
||||
ctags-4.0 (Thu Jun 22 2000)
|
||||
* Fixed infinite loop on certain syntactically invalid class constructs [C++].
|
||||
* Fixed problem of incorrect tags for some pure virtual functions [C++].
|
||||
* Fixed inability to clear all tag types when using --c-types= (all languages).
|
||||
* Fixed problem of arguments to parameterized class being reported as
|
||||
ancestors in the "inherits" extension flag.
|
||||
* Fixed missed tags for typedef-ed function pointers having a PROTO((a,b))
|
||||
style argument list.
|
||||
* Fixed missing file tags for referenced files when using --line-directives
|
||||
option [C/C++].
|
||||
* Fixed failure to recognize drive-qualified file name as a file name when
|
||||
supplied as argument to -I option [Win32].
|
||||
* Fixed problem with missing comma in "inherits" extension flag [Java].
|
||||
* Fixed problem with incorrect or redundant parents listed for "inherits"
|
||||
extension flag [Java].
|
||||
* Added check to avoid recursive symbolic links to directories.
|
||||
* Added warning message for -i option, which is deprecated and being dropped.
|
||||
* Added support for Assembler, COBOL, LISP, PERL, and Scheme, taken from Gnu
|
||||
etags.
|
||||
* Added support for AWK, Bourne/Korn/Z Shell, Python, TCL, and Vim scripts.
|
||||
* Added support for the BETA language, submitted by Erik Corry.
|
||||
* Added ability to determine language from interpreter specified in first line
|
||||
of executable files if they are not recognized by their extension.
|
||||
* Added --options option.
|
||||
* Added ability to specify files having no extension with -h and --langmap
|
||||
options.
|
||||
* Added compile time option to separate path components with a unix-style path
|
||||
separator for sharing tag file across platforms, enabled by defining the
|
||||
label UNIX_PATH_SEPARATOR [Win32].
|
||||
* Fixed portability issues [VMS].
|
||||
|
||||
ctags-3.5.2 (Mon Apr 24 2000)
|
||||
* Fixed problem preventing Emacs-style tags from being written to stdout.
|
||||
|
||||
ctags-3.5.1 (Sun Apr 23 2000)
|
||||
* Fixed infinite loop in writing Emacs-style TAGS file on platforms using
|
||||
tmpnam() instead of mkstemp() [Win32].
|
||||
* Fixed minor problems in Borland makefiles [Win32].
|
||||
* Fixed compiler warning [DJGPP].
|
||||
|
||||
ctags-3.5 (Fri Apr 14 2000)
|
||||
* Fixed core dump when including access field in tag file [Java].
|
||||
* Fixed failure to identify end of statement for block statements [Java].
|
||||
* Fixed bug with lone "end" in feature adaptation part of inheritance clause
|
||||
[Eiffel].
|
||||
* Fixed problem preventing const functions from being recognized as pure
|
||||
virtual [C/C++].
|
||||
* Fixed problem with no tags found after certain macro calls [C/C++].
|
||||
* Fixed bug in descrip.mms build file [VMS].
|
||||
* Changed to use mkstemp() (when available) to create temporary files for
|
||||
security reasons and allow configuring default temporary directory, and to
|
||||
override this directory at run-time by setting TMPDIR environment variable.
|
||||
* Added support for extracting inheritance information into new "inherits"
|
||||
extension flag [C++, Java].
|
||||
* Added Makefile.bc5 for Borland C++ version 5.5 compiler (free version).
|
||||
* Added new question to FAQ regarding Xemacs.
|
||||
* Updated FAQ regarding new release of NEdit.
|
||||
* Renamed Borland 3.1 makefile from Makefile.bcc to Makefile.bc3.
|
||||
* Renamed Microsoft Visual C++ makefile from Makefile.w32 to Makefile.mvc.
|
||||
|
||||
ctags-3.4 (Thu Jan 13 2000)
|
||||
* Fixed sorting problems when LC_ALL environment variable was set to foreign
|
||||
locale (not fixed by previous release).
|
||||
* Fixed nested scoping reported in extension flags and class-qualified tags.
|
||||
* Eliminated generation of class-qualified tag entries when --c-types=+C
|
||||
option is in effect but scope is empty (e.g. "::main").
|
||||
* Added support for default access of class members in Java.
|
||||
* Added new extension flag "implementation", which indicates if a routine or
|
||||
class is virtual or abstract.
|
||||
* Minor changes for OS/2 compilation.
|
||||
|
||||
ctags-3.3.3 (Thu Dec 16 1999)
|
||||
* Changed how input is read for -L and --filter options to permit file names
|
||||
containing spaces (see man page).
|
||||
* Fixed scope recorded for C++ class elements, especially in namespaces.
|
||||
* Fixed spurious tag generated for MODULE PROCEDURE in interfaces [Fortran].
|
||||
* Fixed sorting problems when LC_ALL environment variable was set to foreign
|
||||
locale.
|
||||
* Fixed crash on Windows when compiled with Mingw32 gcc compiler.
|
||||
* Fixed compilation problems on Cray.
|
||||
|
||||
ctags-3.3.2 (Fri Sep 24 1999)
|
||||
* Fixed compile problem on AIX 4.1.
|
||||
* Improved recovery from syntax error [Fortran].
|
||||
* Changed name of configure option (now --enable-custom-config).
|
||||
* Changed Makefile.bcc to optimize for space, since code size exceeded 64KB.
|
||||
|
||||
ctags-3.3.1 (Mon Sep 20 1999)
|
||||
* Fixed segmentation violation occurring when directory recursion was selected.
|
||||
* Fixed misleading message when out of memory during internal sort.
|
||||
|
||||
ctags-3.3 (Fri Sep 17 1999)
|
||||
* Fixed missing class-qualified tags [Java].
|
||||
* Fixed missing tag for functions having function pointer argument [C].
|
||||
* Fixed parsing of conversion functions [C++].
|
||||
* Added missing space following "operator" keyword to the tag names generated
|
||||
for function call operators [C++].
|
||||
* Fixed string parsing to retry file as free source form upon EOF [Fortran].
|
||||
* Fixed missing tags following comments [Fortran].
|
||||
* Fixed missing labels for free source form [Fortran].
|
||||
* Removed 72 character limit for fixed form source lines, since many compilers
|
||||
relax this limit and it is commonly taken advantage of. This was sometimes
|
||||
causing fixed form source to be parsed as free form source [Fortran].
|
||||
* Changed misleading message when file could not be accessed.
|
||||
* Changed behavior of --verbose option to display option processing.
|
||||
* Changed -I option to permit clearing the token list with "-I-".
|
||||
* Changed --lang option to accept new "auto" parameter.
|
||||
* Changed --langmap option to accept new "default" parameter.
|
||||
* Changed --eiffel-types option to accept new 'C' flag to generate
|
||||
class-qualified tags.
|
||||
* Changed -h option to accept new "default" parameter.
|
||||
* Changed option processing. Most options may now appear anywhere on the
|
||||
command line, affecting only those files which follow them.
|
||||
* Added ability to specify default options in any of the files /etc/ctags.conf,
|
||||
/usr/local/etc/ctags.conf, $HOME/.ctags, .ctags, and one optional file,
|
||||
which can be supplied at configure time.
|
||||
* Added --filter option.
|
||||
* Added --filter-terminator option.
|
||||
|
||||
ctags-3.2.4 (Thu Jul 01 1999)
|
||||
* Changed name of macro in Makefile.in to avoid being overriden by CTAGS
|
||||
environment variable.
|
||||
|
||||
ctags-3.2.3 (Mon Jun 21 1999)
|
||||
* Small portability change for EMX compiler on OS/2.
|
||||
* Slight change to W32 and BCC makefiles.
|
||||
|
||||
ctags-3.2.2 (Sat May 29 1999)
|
||||
* Fixed endless error loop in the case of unreadable file.
|
||||
* Fixed redundant include entries in TAGS file when using --etags-include.
|
||||
|
||||
ctags-3.2.1 (Wed May 09 1999)
|
||||
* Fixed problem reading -I token list from file.
|
||||
* Fixed with "using" declarations which corrupted tag file [C++].
|
||||
* Fixed configure.in to more reliably recognize existing prototypes.
|
||||
* Added ability to ignore preprocessor directives in Fortran files.
|
||||
* Added support for egcs/MingW32 compiler [Win32].
|
||||
|
||||
ctags-3.2 (Wed Mar 03 1999)
|
||||
* Fixed spurious tags related to export specifiers of feature clauses [Eiffel].
|
||||
* Fixed problem with template in ctor-initialer [C++].
|
||||
* Fixed typo causing compiler error [MSVC].
|
||||
* Extended -I option to allow token replacement [thanks to Flemming Madsen].
|
||||
* Added --etags-include option to support TAGS file includes.
|
||||
* Added support for QDOS [thanks to Thierry Godefroy].
|
||||
|
||||
ctags-3.1.2 (Tue Jan 26 1999)
|
||||
* Changed extension flags to eliminate space between label and value to remain
|
||||
true to the intent of the agreement on the extended format made with editor
|
||||
authors.
|
||||
* Added --links option to permit ignoring symbolic links.
|
||||
* Fixed missing tags upon ANSI style variable function argument lists.
|
||||
* Fixed missing tags for methods with fully qualified type names in argument
|
||||
list [Java].
|
||||
* Fixed double tags generated for enumerators followed by comma.
|
||||
* Fixed missing path prefix for -p option [Win 95/NT].
|
||||
|
||||
ctags-3.1 (Wed Jan 20 1999)
|
||||
* Changed -h and -langmap options to accept a plus sign as the first character
|
||||
of their arguments to indicate that arguments should be added to current.
|
||||
* Changed default for member tags to 'on' [C/C++].
|
||||
* Changed default for local entities to 'off' [Eiffel].
|
||||
* Added tags for forward class/struct/union/enum declarations when using
|
||||
-c-types=+x [C/C++].
|
||||
* Fixed memory overwrite bug causing general protection fault [Win 95/NT].
|
||||
* Fixed missing tags for methods with throws clause [Java].
|
||||
* Fixed bad tags generated for null macro names [C].
|
||||
* Fixed spurious tag for features and entities of BIT type [Eiffel].
|
||||
* Fixed spurious tags when local entity declaration list was empty [Eiffel].
|
||||
* Fixed missing tags for contructors and destructors [C++].
|
||||
* Fixed failure to recognize function when declaration for first argument
|
||||
was of template type [C++].
|
||||
|
||||
ctags-3.0.3 (Mon Dec 21 1998)
|
||||
* Fixed mistake made in previous version which caused macro tags to be missed.
|
||||
* Fixed parsing of --langmap option.
|
||||
|
||||
ctags-3.0.2 (Mon Dec 21 1998)
|
||||
* Added tags for names undefined with #undef [C/C++].
|
||||
* Added tags for renamed features (Eiffel).
|
||||
* Improved Emacs-style tag file contents (per Ian Zimmerman).
|
||||
* Fixed problem handling deferred, external, once, obsolete features in Eiffel.
|
||||
* Fixed porting problem [OSF1 V4.0].
|
||||
|
||||
ctags-3.0.1 (Sat Dec 12 1998)
|
||||
* Fixed problem with certain macros and functions with no declared return type.
|
||||
* Fixed problem causing endless loop on MSDOS/Win32 by restoring use of binary
|
||||
mode on opening of source files.
|
||||
* Fixed porting problems [SunOS 4.1.x and MSVC++ 5.0].
|
||||
|
||||
ctags-3.0 (Sun Dec 06 1998)
|
||||
* Added support for the Eiffel language (everyone should learn Eiffel).
|
||||
* Added support for the Fortran language.
|
||||
* Added --c-types option to specify tag types to be included for C/C++.
|
||||
* Added --eiffel-types option to specify tag types to be included for Eiffel.
|
||||
* Added --fortran-types option to specify tag types to be included for Fortran.
|
||||
* Added --file-scope option to place verbose tag description into tag file.
|
||||
* Added --file-tags option to place tags for source file names into tag file.
|
||||
* Added --java-types option to specify tag types to be included for Java.
|
||||
* Added --kind-long option to place verbose tag description into tag file.
|
||||
* Added --linedirectives option to enable processing of #line directives so
|
||||
that running ctags on preprocessor output can generate line numbers and file
|
||||
names which correspond to the original source files.
|
||||
* Added -V option to enable verbose message for each file considered.
|
||||
* Added special handling for macros of form "INIT(= value)".
|
||||
* Added ability to suffix an ignored identifier (-I option) with the '+'
|
||||
character, thus instructing ctags to also ignore any argument list which
|
||||
may follow the identifier.
|
||||
* Changed the -i option, moving Java language options to the new --java-types
|
||||
option. The -i option is now deprecated in favor of the new language
|
||||
specific tag type options.
|
||||
* Changed behavior of handling of ignored identifiers (-I option) to still
|
||||
generate a tag for any macro definition for that identifier.
|
||||
* Changed handling of -h option so that include files are no longer assumed to
|
||||
be C++ files.
|
||||
* Changed tags for operators to always precede the operator with the string
|
||||
"operator ", thus making it consistent for all operators. [C++]
|
||||
* Changed C/C++ parsing, catching many more tricky constructs.
|
||||
* Changed extension flags to place a space between the label and the value for
|
||||
readability.
|
||||
* Fixed core dump which occurred when using -iF (now --file-tags) together
|
||||
with -e (etags) on a zero-length file.
|
||||
* Fixed missing or incorrect tags for conversions operators or operator "()".
|
||||
* Fixed incorrect parent class in extension flags for type declarations of the
|
||||
form "class Bar { OtherClass::sometype foo; }".
|
||||
* Fixed missing tags for "friend" and "static" prototypes in header files.
|
||||
* Fixed problem of external "sort" reporting locale not available on HPUX.
|
||||
* Fixed -p option.
|
||||
* Fixed VMS support. It should now work for any source file type.
|
||||
|
||||
ctags-2.3.2 (Wed Sep 09 1998)
|
||||
* Fixed -h option; broken since version 1.7, yet only just reported.
|
||||
|
||||
ctags-2.3.1 (Sun Aug 30 1998)
|
||||
* Fixed improper handling of derived structs.
|
||||
* Fixed wrong class name tag when a nested-name-specifier was present in class
|
||||
declaration.
|
||||
* Added parent information into tag extension flags for data structures to
|
||||
match that already present for members.
|
||||
* Add missing documentation for --langmap option in the --help output.
|
||||
* Eliminated compiler warning [gcc 2.8.1].
|
||||
|
||||
ctags-2.3 (Thu Aug 20 1998)
|
||||
* Eliminated compiler warnings [SGI MIPSpro].
|
||||
|
||||
ctags-2.2.7 (Mon Aug 17 1998)
|
||||
* Fixed porting problem [Borland C++].
|
||||
|
||||
ctags-2.2.6 (Wed Aug 12 1998)
|
||||
* Fixed core dump encountered on some platforms when the CTAGS environment
|
||||
variable was set but empty.
|
||||
* Fixed porting problem [MSVC].
|
||||
* Added directory recursion support for Amiga.
|
||||
|
||||
ctags-2.2.3 (Sun Aug 02 1998)
|
||||
ctags-2.2.2 (Fri Jul 24 1998)
|
||||
* Fixed porting problems [AIX, HP-UX, OSF/1, SunOS, MSVC].
|
||||
|
||||
ctags-2.2.1 (Fri Jul 24 1998)
|
||||
* Now uses a default directory name of "." when using -R or --recurse option
|
||||
(e.g. "ctags -R" is equivalent to "ctags -R .").
|
||||
* Directories named "SCCS" are skipped when using the -R or --recurse option
|
||||
under Unix.
|
||||
* Fixed porting problems [HP-UX, IRIX, SunOS, MSDOS/Windows].
|
||||
|
||||
ctags-2.2 (Mon Jul 20 1998)
|
||||
* Added the --recurse and -R options to allow recursing into directories.
|
||||
This allows running ctags on an entire source directory tree using the
|
||||
single command "ctags -R <dir>". Currently, this option is only supported on
|
||||
UNIX, MSDOS, Windows 95/NT, and OS/2. Other platforms will have to wait.
|
||||
* Changed writing of Emacs-style TAGS file to binary mode [MSDOS/Windows].
|
||||
* Fixed porting problems [HP-UX, OSF/1].
|
||||
|
||||
ctags-2.1.1 (Mon Jul 06 1998)
|
||||
* Changed -h option to allow only periods to separate extensions.
|
||||
* Added the --langmap option to allow overriding the default associations
|
||||
between source language and file extension.
|
||||
* Added configuration check and code work-around for putenv() prototypes
|
||||
missing the const from the argument declaration [IRIX 5.2 and CRAY J90].
|
||||
* Added makefile for VMS.
|
||||
* Fixed porting problem [HP-UX].
|
||||
|
||||
ctags-2.1 (Wed Jul 01 1998)
|
||||
* Added Java support.
|
||||
* Eliminated the --keywords option introduced in the 2.0.4, replacing it with
|
||||
automatic detection of the language (i.e. recognized keywords) based upon
|
||||
the file extension.
|
||||
* Added the --lang option for manually overriding the automatic selection of
|
||||
the language.
|
||||
* Added new flag 'i' to the -i option to allow generating tags for Java
|
||||
interfaces.
|
||||
* Added new flag 'n' to the -i option to allow generating tags for C++
|
||||
namespaces.
|
||||
* Added new flag 'x' to the -i option to allow generating tags for extern
|
||||
variable declarations.
|
||||
* Added new extension flags, "private", "protected", and "public", which
|
||||
indicate the visibility of class members when it can be determined.
|
||||
* Changed behavior of flag 'C' of the -i option to add tags of form
|
||||
"class.member" for Java.
|
||||
* Changed how files on command line are handled. Ctags will now only scan
|
||||
those files whose extensions it knows about unless the --lang option is
|
||||
specified. This allows running ctags on all files in a directory without
|
||||
having to be specific (e.g. "ctags *").
|
||||
* Removed support for duplicate tag warnings and the -w and -W options. These
|
||||
options are silently ignored for now.
|
||||
|
||||
ctags-2.0.4 (Sat May 23 1998)
|
||||
* Added sorting time to the output of the --totals option.
|
||||
* Added the --keywords option to allow restricting the recognized
|
||||
declaration keywords in order to handle legacy source code which uses
|
||||
newer keywords for variable and parameter names.
|
||||
* Ignore list now also applies to macro tags.
|
||||
* /dev/stdout now properly handled as parameter to -f/-o option.
|
||||
* Fixed problem handling an operator definition in C++ when white space
|
||||
appeared between the "operator" keyword and the operator (e.g. "=").
|
||||
* Fixed handling of non-symbolic operators (e.g. "new", "delete", etc.).
|
||||
* Fixed sort order problem for some locale settings.
|
||||
* Fixed segmentation violation when using ignore list (-I) on SunOS 4.x.
|
||||
* Fixed a segmentation violation caused by a stack overwrite when testing a
|
||||
particular kind of non-standard tag file format.
|
||||
|
||||
ctags-2.0.3 (Sun Mar 12 1998)
|
||||
* Added configure check for "strip" program.
|
||||
* Added new flag 'C' to the -i option to allow adding extra tags to the tag
|
||||
file in the form "class::member" for class methods and members.
|
||||
|
||||
ctags-2.0.2 (Wed Feb 25 1998)
|
||||
* Added stripping of installed binary for "install" target.
|
||||
|
||||
ctags-2.0.1 (Thu Feb 19 1998)
|
||||
* Added support for C++.
|
||||
* Added new flag 'F' to the -i option to allow adding an extra tag for the
|
||||
basename of each source file supplied to ctags. This provides the ability to
|
||||
jump to a source file in Vi using ":tag file.c".
|
||||
* Added new flag 'm' to generate tags for class, structure, and union members
|
||||
(disabled by default).
|
||||
* Added several new flags to the -i option to allow finer specification of
|
||||
which types of tags to include/exclude.
|
||||
* Added ".hh" extension to the default list of files recognized as header
|
||||
files.
|
||||
* Added explicit handling of special gcc construct __attribute((..)),
|
||||
which could lead to incorrect tag generation.
|
||||
* Added configure option --disable-extended-format to allow building ctags
|
||||
with the extended format disabled by default.
|
||||
* Added configure option --enable-macro-patterns to change the default
|
||||
behavior of ctags to generate patterns instead of line numbers for macro
|
||||
(define) tags.
|
||||
* Changed configure option --enable-internal-sort to --disable-external-sort.
|
||||
* Changed makefile for OS/2.
|
||||
* Removed support for the -d, -t and -T options which had been deprecated
|
||||
for some time.
|
||||
* Removed ANNOUNCE file in distribution, consolidating it with the README
|
||||
file.
|
||||
* Replaced CHANGES file with NEWS for more GNU-like standard distribution.
|
||||
* Improved the detection of macros of the type generated by Microsoft Visual C
|
||||
when generating source code. These caused subsequent statements to fail to
|
||||
have tags generated for them. Still not bullet proof, though.
|
||||
* Fixed a problem which prevented the use of / as a path separator under MSDOS
|
||||
and Win 95/NT.
|
||||
* Fixed problem of blank lines occuring in the tag file.
|
||||
* Fixed recognition of declarations with parentheses.
|
||||
* Fixed problem of missing tags for objects within extern "C" blocks.
|
||||
* Fixed problem in source file counts when using --totals option.
|
||||
* Extended the length of tag type field in -x output to be more verbose.
|
||||
* Fixed option initialization error which caused static tags to be excluded.
|
||||
|
||||
ctags-1.7 (Mon Oct 13 1997)
|
||||
* Tag files now use a new extended format which is backwards compatible with
|
||||
existing Vi implementations, yet provides extended information which can be
|
||||
used by supporting editors.
|
||||
* Added documentation pseudo-tags (e.g. !_TAG_FILE_FORMAT) to tag file.
|
||||
* Added the --excmd option as alternative to the -n and -N options.
|
||||
* Added the --format option to allow forcing the old-style tag file format.
|
||||
* Added the --if0 to control how "#if 0" branches are handled.
|
||||
* Added the --sort option as alternative to -u option.
|
||||
* Added the --totals option to print statistics of tag generation.
|
||||
* Added the --version option.
|
||||
* Improved handling of preprocessor conditionals.
|
||||
* Code within an #if 0 is now never scanned for non-macro tags by default
|
||||
since tags within that code could conceivably be overridden by more
|
||||
desirable tags in the #else branch to follow. Macro tags in these branches
|
||||
are always included.
|
||||
* Fixed problem which could leave invalid tag entries in tag file when an
|
||||
internal re-scan of a source file occurred.
|
||||
* Fixed problem with internal sort mechanism when appending tags to existing
|
||||
file.
|
||||
* Changed external sort command to filter out duplicate identical tags lines
|
||||
(including the pattern) unless warnings for duplicate tags are enabled.
|
||||
* Added data to emacs style tag entries to more closely match that produced by
|
||||
the GNU etags.
|
||||
* Removed fixed limits on maximum tag line length.
|
||||
* Correction to Amiga makefile.
|
||||
* Improvements to Win32 portability with changes to Makefile.w32.
|
||||
|
||||
ctags-1.6 (Tue May 13 1997)
|
||||
* Now using GNU autoconf to permit automatic host configuration.
|
||||
* Added the -e option to generate Emacs style tag files
|
||||
* Added ETAGS environment variable to be used when -e option is in effect.
|
||||
* Added the -p option to supply a default path for source files.
|
||||
* Fixed problem of incorrect line numbers in tag file when form feeds and
|
||||
vertical tabs occured in the source file.
|
||||
* Fixed problem preventing ignoring of identifiers specified via the -I option
|
||||
when the identifiers were followed by a parameter list in the source code.
|
||||
* Changed the search patterns generated for #define macros when using -N
|
||||
option. It now includes the character following the name (or "$" if end of
|
||||
line) instead of the "\>" used previously. Some editors did not support this
|
||||
metacharacter for tag searches.
|
||||
* Removed -u (unique) flag from sort command line invocation to retain exactly
|
||||
indentical tag patterns in the same file. This is in preparation for editor
|
||||
support of duplicate tags.
|
||||
* Fixed problem resulting in no tags generated following a function-like
|
||||
macro reference outside of a function block.
|
||||
* Fixed problem of no tags generated for typedef for function or function
|
||||
pointer.
|
||||
* Fixed problem of no tags generated after encountering strange function
|
||||
declarations (e.g. "foo(params) saywhat (void);")
|
||||
|
||||
ctags-1.5 (Sat Oct 5 1996)
|
||||
* Added generation of tags for objects inside C++ extern blocks (e.g.
|
||||
'extern "C" {...}' construct).
|
||||
* Added generation of tags for function definitions inside brace-enclosed
|
||||
("{}") blocks, since function definitions are allowed inside classes and
|
||||
extern blocks.
|
||||
* Added the -N option to force patterns to be used for all tags (including
|
||||
macro tags).
|
||||
* Changed the search patterns generated for macro definitions to be terminated
|
||||
with "\>", which allows the pattern to still match even when the #define
|
||||
line beyond the macro name is changed.
|
||||
* Fixed problem resulting in no tags generated for files containing a
|
||||
function-like macro, such as 'MODULE_ID("$Id")', even when ignoring the
|
||||
keyword. This also fixed a problem that caused tags to be missed for
|
||||
initialized function pointer definitions.
|
||||
* Redirected error messages to stdout for MSDOS version.
|
||||
|
||||
ctags-1.4 (Sun Aug 18 1996)
|
||||
* Added recursive parsing of class/struct/enum blocks to look for
|
||||
class/struct/enum tags and enumeration values.
|
||||
* Added the -I option to specify keywords to ignore in the source files.
|
||||
* Fixed problem resulting in no tag generated when declaring a pointer const
|
||||
or volatile.
|
||||
* Fixed problem resulting in no tag generated for comma terminated function
|
||||
declarations.
|
||||
|
||||
ctags-1.3 (Sun Jun 16 1996)
|
||||
* Fixed problem problem which caused "struct tag;" to be misinterpreted as a
|
||||
variable definition.
|
||||
* Added the -n option to use line numbers in the tag file instead of patterns.
|
||||
* Added the -? option as an alternative for the --help option.
|
||||
|
||||
ctags-1.2 (Wed Jun 5 1996)
|
||||
* Fixed a problem caused by an unitialized variable.
|
||||
|
||||
ctags-1.1 (Tue Jun 4 1996)
|
||||
* Fixed problem reading parameter list to the -h option.
|
||||
|
||||
ctags-1.0a (Mon Jun 3 1996)
|
||||
* Added ctags.lsm to distribution
|
||||
|
||||
ctags-1.0 (Fri May 31 1996)
|
||||
* First public release.
|
||||
|
||||
vim:tw=78
|
||||
@@ -0,0 +1,73 @@
|
||||
Exuberant Ctags
|
||||
===============
|
||||
Author: Darren Hiebert <dhiebert at users.sourceforge.net>
|
||||
http://ctags.sourceforge.net
|
||||
Instant Messaging:
|
||||
Yahoo! ID : dbhiebert
|
||||
AIM ScreenName: darrenhiebert
|
||||
|
||||
Exuberant Ctags is a multilanguage reimplementation of the much-underused
|
||||
ctags(1) program and is intended to be the mother of all ctags programs. It
|
||||
generates indexes of source code definitions which are used by a number of
|
||||
editors and tools. The motivation which drove the development of Exuberant
|
||||
Ctags was the need for a ctags program which supported generation of tags
|
||||
for all possible C language constructs (which no other ctags offers), and
|
||||
because most were easily fooled by a number of preprocessor contructs.
|
||||
|
||||
|
||||
Exuberant Ctags offers the following features:
|
||||
|
||||
1. It supports the following languages: Assembler, AWK, ASP, BETA,
|
||||
Bourne/Korn/Z Shell, C, C++, C#, COBOL, Eiffel, Erlang, Fortran, Java, Lisp,
|
||||
Lua, Makefile, Pascal, Perl, PHP, PL/SQL, Python, REXX, Ruby, Scheme,
|
||||
S-Lang, SML (Standard ML), Tcl, Vera, Verilog, Vim, and YACC.
|
||||
|
||||
2. It is capable of generating tags for virtually all C language constructs.
|
||||
|
||||
3. It is very robust in parsing code. In particular, the C/C++ parser is
|
||||
far less easily fooled by code containing #if preprocessor conditional
|
||||
constructs, using a conditional path selection algorithm to resolve
|
||||
complicated situations, and a fall-back algorithm when this one fails.
|
||||
|
||||
4. Supports output of Emacs-style TAGS files (i.e. "etags").
|
||||
|
||||
5. User-defined languages, using Posix regular expressions.
|
||||
|
||||
6. Supports UNIX, MSDOS, Windows 95/98/NT/2000/XP, OS/2, QNX, Amiga, QDOS,
|
||||
RISC OS, VMS, Macintosh, and Cray. Some pre-compiled binaries are
|
||||
available on the web site.
|
||||
|
||||
|
||||
Visit the Exuberant Ctags web site:
|
||||
|
||||
http://ctags.sourceforge.net
|
||||
|
||||
|
||||
Which brings us to the most obvious question:
|
||||
|
||||
Q: Why is it called "Exuberant" ctags?
|
||||
A: Because one of the meanings of the word is:
|
||||
|
||||
exuberant : produced in extreme abundance : PLENTIFUL syn see PROFUSE
|
||||
|
||||
Compare the tag file produced by Exuberant Ctags with that produced by any
|
||||
other ctags and you will see how appropriate the name is.
|
||||
|
||||
|
||||
This source code is distributed according to the terms of the GNU General
|
||||
Public License. It is provided on an as-is basis and no responsibility is
|
||||
accepted for its failure to perform as expected. It is worth at least as
|
||||
much as you paid for it!
|
||||
|
||||
Exuberant Ctags was originally derived from and inspired by the ctags
|
||||
program by Steve Kirkendall ([email protected]) that comes with the Elvis
|
||||
vi clone (though almost none of the original code remains). This, too, is
|
||||
freely available.
|
||||
|
||||
Please report any problems you find. The two problems I expect to be most
|
||||
likely are either a tag which you expected but is missing, or a tag created
|
||||
in error (shouldn't really be a tag). Please include a sample of code (the
|
||||
definition) for the object which misbehaves.
|
||||
|
||||
--
|
||||
vim:tw=76:sw=4:et:
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* $Id: acconfig.h,v 1.4 2003/04/01 05:02:21 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains input source for generating config.h.in
|
||||
*/
|
||||
|
||||
/* Package name.
|
||||
*/
|
||||
#undef PACKAGE
|
||||
|
||||
/* Package version.
|
||||
*/
|
||||
#undef VERSION
|
||||
|
||||
/* Define to the appropriate type if <time.h> does not define this.
|
||||
*/
|
||||
#undef clock_t
|
||||
|
||||
/* Define to long if <stdio.h> does not define this.
|
||||
*/
|
||||
#undef fpos_t
|
||||
|
||||
/* Define to the appropriate size for tmpnam() if <stdio.h> does not define
|
||||
* this.
|
||||
*/
|
||||
#undef L_tmpnam
|
||||
|
||||
/* Define this macro if the field "st_ino" exists in struct stat in
|
||||
* <sys/stat.h>.
|
||||
* */
|
||||
#undef HAVE_STAT_ST_INO
|
||||
|
||||
/* Define remove to unlink if you have unlink(), but not remove().
|
||||
*/
|
||||
#undef remove
|
||||
|
||||
/* Define this value used by fseek() appropriately if <stdio.h>
|
||||
* (or <unistd.h> on SunOS 4.1.x) does not define them.
|
||||
*/
|
||||
#undef SEEK_SET
|
||||
|
||||
/* Define as the maximum integer on your system if not defined <limits.h>.
|
||||
*/
|
||||
#undef INT_MAX
|
||||
|
||||
/* You can define this label to be a string containing the name of a
|
||||
* site-specific configuration file containing site-wide default options. The
|
||||
* files /etc/ctags.conf and /usr/local/etc/ctags.conf are already checked,
|
||||
* so only define one here if you need a file somewhere else.
|
||||
*/
|
||||
#undef CUSTOM_CONFIGURATION_FILE
|
||||
|
||||
/* Define this label if you want macro tags (defined lables) to use patterns
|
||||
* in the EX command by default (original ctags behavior is to use line
|
||||
* numbers).
|
||||
*/
|
||||
#undef MACROS_USE_PATTERNS
|
||||
|
||||
/* Define this as desired.
|
||||
* 1: Original ctags format
|
||||
* 2: Extended ctags format with extension flags in EX-style comment.
|
||||
*/
|
||||
#define DEFAULT_FILE_FORMAT 2
|
||||
|
||||
/* Define this label if your system supports starting scripts with a line of
|
||||
* the form "#! /bin/sh" to select the interpreter to use for the script.
|
||||
*/
|
||||
#undef SYS_INTERPRETER
|
||||
|
||||
/* Define this label if your system uses case-insensitive file names
|
||||
*/
|
||||
#undef CASE_INSENSITIVE_FILENAMES
|
||||
|
||||
/* Define this label to use the system sort utility (which is probably more
|
||||
* efficient) over the internal sorting algorithm.
|
||||
*/
|
||||
#ifndef INTERNAL_SORT
|
||||
# undef EXTERNAL_SORT
|
||||
#endif
|
||||
|
||||
/* If you wish to change the directory in which temporary files are stored,
|
||||
* define this label to the directory desired.
|
||||
*/
|
||||
#undef TMPDIR
|
||||
|
||||
/* Define this label if regcomp() is broken.
|
||||
*/
|
||||
#undef REGCOMP_BROKEN
|
||||
|
||||
/* Define this label if you wish to check the regcomp() function at run time
|
||||
* for correct behavior. This function is currently broken on Cygwin.
|
||||
*/
|
||||
#undef CHECK_REGCOMP
|
||||
|
||||
/* This corrects the problem of missing prototypes for certain functions
|
||||
* in some GNU installations (e.g. SunOS 4.1.x).
|
||||
*/
|
||||
#undef __USE_FIXED_PROTOTYPES__
|
||||
|
||||
/* Define this is you have a prototype for putenv() in <stdlib.h>, but
|
||||
* doesn't declare its argument as "const char *".
|
||||
*/
|
||||
#undef NON_CONST_PUTENV_PROTOTYPE
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing
|
||||
* a prototype for, or a type mismatch using, one of the following functions,
|
||||
* define the appropriate label and remake.
|
||||
*/
|
||||
#undef NEED_PROTO_REMOVE
|
||||
#undef NEED_PROTO_UNLINK
|
||||
#undef NEED_PROTO_MALLOC
|
||||
#undef NEED_PROTO_GETENV
|
||||
#undef NEED_PROTO_FGETPOS
|
||||
#undef NEED_PROTO_STAT
|
||||
#undef NEED_PROTO_LSTAT
|
||||
#undef NEED_PROTO_TRUNCATE
|
||||
#undef NEED_PROTO_FTRUNCATE
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
- Lines below this are automatically generated by autoheader
|
||||
----------------------------------------------------------------------------*/
|
||||
/* @TOP@ */
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* $Id: argproc.c,v 1.2 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1989, Mark Pizzolato ([email protected])
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Provided by Stephen P. Wall <[email protected]>
|
||||
* Extracted from the VMS port of GNU patch-2.1.
|
||||
*
|
||||
* This module provides redirection support for the VAX DECC port of
|
||||
* Exuberant Ctags.
|
||||
*/
|
||||
/*
|
||||
* @(#)argproc.c 1.0 89/02/01 Mark Pizzolato ([email protected])
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
char argproc_version [] = "@(#)argproc.c VMS uucp Version infopiz-1.0";
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <descrip.h>
|
||||
#include <dvidef.h>
|
||||
#include <errno.h>
|
||||
#include <iodef.h>
|
||||
#include <lib$routines.h>
|
||||
#include <starlet.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syidef.h> /* System Information Definitions */
|
||||
|
||||
#define EXIT_OK 1 /* image exit code */
|
||||
#define EXIT_ERR 0x10000000 /* image exit code */
|
||||
|
||||
/*
|
||||
* getredirection() is intended to aid in porting C programs
|
||||
* to VMS (Vax-11 C) which does not support '>' and '<'
|
||||
* I/O redirection, along with a command line pipe mechanism
|
||||
* using the '|' AND background command execution '&'.
|
||||
* The piping mechanism will probably work with almost any 'filter' type
|
||||
* of program. With suitable modification, it may useful for other
|
||||
* portability problems as well.
|
||||
*
|
||||
* Author: Mark Pizzolato [email protected]
|
||||
* Mods: Steve Wall Don't return a full path unless the
|
||||
* original filename included a path.
|
||||
*/
|
||||
struct list_item
|
||||
{
|
||||
struct list_item *next;
|
||||
char *value;
|
||||
};
|
||||
|
||||
static expand_wild_cards ();
|
||||
static char *pipe_and_fork ();
|
||||
|
||||
int
|
||||
getredirection (ac, av)
|
||||
int *ac;
|
||||
char ***av;
|
||||
/*
|
||||
* Process vms redirection arg's. Exit if any error is seen.
|
||||
* If getredirection() processes an argument, it is erased
|
||||
* from the vector. getredirection () returns a new argc and argv value.
|
||||
* In the event that a background command is requested (by a trailing "&"),
|
||||
* this routine creates a background subprocess, and simply exits the program.
|
||||
*
|
||||
* Warning: do not try to simplify the code for vms. The code
|
||||
* presupposes that getredirection() is called before any data is
|
||||
* read from stdin or written to stdout.
|
||||
*
|
||||
* Normal usage is as follows:
|
||||
*
|
||||
* main (argc, argv)
|
||||
* int argc;
|
||||
* char *argv [];
|
||||
* {
|
||||
* getredirection (&argc, &argv);
|
||||
* }
|
||||
*/
|
||||
{
|
||||
int argc = *ac; /* Argument Count */
|
||||
char **argv = *av; /* Argument Vector */
|
||||
char *ap; /* Argument pointer */
|
||||
int j; /* argv [] index */
|
||||
extern int errno; /* Last vms i/o error */
|
||||
int item_count = 0; /* Count of Items in List */
|
||||
struct list_item *list_head = 0; /* First Item in List */
|
||||
struct list_item *list_tail; /* Last Item in List */
|
||||
char *in = NULL; /* Input File Name */
|
||||
char *out = NULL; /* Output File Name */
|
||||
char *outmode = "w"; /* Mode to Open Output File */
|
||||
int cmargc = 0; /* Piped Command Arg Count */
|
||||
char **cmargv = NULL;/* Piped Command Arg Vector */
|
||||
|
||||
/*
|
||||
* First handle the case where the last thing on the line ends with
|
||||
* a '&'. This indicates the desire for the command to be run in a
|
||||
* subprocess, so we satisfy that desire.
|
||||
*/
|
||||
{
|
||||
extern background_process ();
|
||||
ap = argv [argc-1];
|
||||
if (0 == strcmp ("&", ap))
|
||||
exit (background_process (--argc, argv));
|
||||
if ('&' == ap [strlen (ap)-1])
|
||||
{
|
||||
ap [strlen (ap)-1] = '\0';
|
||||
exit (background_process (argc, argv));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now we handle the general redirection cases that involve '>', '>>',
|
||||
* '<', and pipes '|'.
|
||||
*/
|
||||
for (j = 0; j < argc; ++j)
|
||||
{
|
||||
if (0 == strcmp ("<", argv [j]))
|
||||
{
|
||||
if (j+1 >= argc)
|
||||
{
|
||||
errno = EINVAL;
|
||||
perror ("No input file");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
in = argv [++j];
|
||||
continue;
|
||||
}
|
||||
if ('<' == *(ap = argv [j]))
|
||||
{
|
||||
in = 1 + ap;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp (">", ap))
|
||||
{
|
||||
if (j+1 >= argc)
|
||||
{
|
||||
errno = EINVAL;
|
||||
perror ("No output file");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
out = argv [++j];
|
||||
continue;
|
||||
}
|
||||
if ('>' == *ap)
|
||||
{
|
||||
if ('>' == ap [1])
|
||||
{
|
||||
outmode = "a";
|
||||
if ('\0' == ap [2])
|
||||
out = argv [++j];
|
||||
else
|
||||
out = 2 + ap;
|
||||
}
|
||||
else
|
||||
out = 1 + ap;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp ("|", argv [j]))
|
||||
{
|
||||
if (j+1 >= argc)
|
||||
{
|
||||
errno = EPIPE;
|
||||
perror ("No command to Pipe to");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
cmargc = argc- (j+1);
|
||||
cmargv = &argv [j+1];
|
||||
argc = j;
|
||||
continue;
|
||||
}
|
||||
if ('|' == *(ap = argv [j]))
|
||||
{
|
||||
++argv [j];
|
||||
cmargc = argc-j;
|
||||
cmargv = &argv [j];
|
||||
argc = j;
|
||||
continue;
|
||||
}
|
||||
expand_wild_cards (ap, &list_head, &list_tail, &item_count);
|
||||
}
|
||||
/*
|
||||
* Allocate and fill in the new argument vector, Some Unix's terminate
|
||||
* the list with an extra null pointer.
|
||||
*/
|
||||
argv = *av = calloc (item_count+1, sizeof (char *));
|
||||
for (j = 0; j < item_count; ++j, list_head = list_head->next)
|
||||
argv [j] = list_head->value;
|
||||
*ac = item_count;
|
||||
if (cmargv != NULL)
|
||||
{
|
||||
char subcmd [1024];
|
||||
|
||||
if (out != NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
perror ("Invalid '|' and '>' specified");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
strcpy (subcmd, cmargv [0]);
|
||||
for (j = 1; j < cmargc; ++j)
|
||||
{
|
||||
strcat (subcmd, " \"");
|
||||
strcat (subcmd, cmargv [j]);
|
||||
strcat (subcmd, "\"");
|
||||
}
|
||||
out = pipe_and_fork (subcmd);
|
||||
}
|
||||
if ((in != NULL) && (NULL == freopen (in, "r", stdin, "mbc=32", "mbf=2")))
|
||||
{
|
||||
perror (in); /* Can't find file */
|
||||
exit (EXIT_ERR); /* Is a fatal error */
|
||||
}
|
||||
if ((out != NULL) && (NULL == freopen (out, outmode, stdout, "mbc=32", "mbf=2")))
|
||||
{
|
||||
perror (ap); /* Error, can't write or append */
|
||||
exit (EXIT_ERR); /* Is a fatal error */
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "Arglist:\n");
|
||||
for (j = 0; j < *ac; ++j)
|
||||
fprintf (stderr, "argv[%d] = '%s'\n", j, argv [j]);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static add_item (head, tail, value, count)
|
||||
struct list_item **head;
|
||||
struct list_item **tail;
|
||||
char *value;
|
||||
int *count;
|
||||
{
|
||||
if (*head == 0)
|
||||
{
|
||||
if (NULL == (*head = calloc (1, sizeof (**head))))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
perror ("");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
*tail = *head;
|
||||
}
|
||||
else
|
||||
if (NULL == ((*tail)->next = calloc (1, sizeof (**head))))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
perror ("");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
else
|
||||
*tail = (*tail)->next;
|
||||
(*tail)->value = value;
|
||||
++ (*count);
|
||||
}
|
||||
|
||||
static expand_wild_cards (item, head, tail, count)
|
||||
char *item;
|
||||
struct list_item **head;
|
||||
struct list_item **tail;
|
||||
int *count;
|
||||
{
|
||||
int expcount = 0;
|
||||
int context = 0;
|
||||
int status;
|
||||
int status_value;
|
||||
char *had_version;
|
||||
int had_path;
|
||||
$DESCRIPTOR (filespec, item);
|
||||
/*$DESCRIPTOR (defaultspec, "SYS$DISK:[]*.*;");*/
|
||||
$DESCRIPTOR (defaultspec, "");
|
||||
$DESCRIPTOR (resultspec, "");
|
||||
|
||||
if (strcspn (item, "*%") == strlen (item))
|
||||
{
|
||||
add_item (head, tail, item, count);
|
||||
return;
|
||||
}
|
||||
resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
|
||||
resultspec.dsc$b_class = DSC$K_CLASS_D;
|
||||
resultspec.dsc$a_pointer = NULL;
|
||||
filespec.dsc$w_length = strlen (item);
|
||||
/*
|
||||
* Only return version specs, if the caller specified a version
|
||||
*/
|
||||
had_version = strchr (item, ';');
|
||||
/*
|
||||
* Only return full path if the caller specified a path
|
||||
*/
|
||||
had_path = (strchr (item, ']') || strchr (item, ':'));
|
||||
while (1 == (1&lib$find_file (&filespec, &resultspec, &context,
|
||||
&defaultspec, 0, &status_value, &0)))
|
||||
{
|
||||
char *string;
|
||||
char *c;
|
||||
|
||||
if (NULL == (string = calloc (1, resultspec.dsc$w_length+1)))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
perror ("");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
strncpy (string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
|
||||
string [resultspec.dsc$w_length] = '\0';
|
||||
if (NULL == had_version)
|
||||
*((char *) strrchr (string, ';')) = '\0';
|
||||
if (!had_path) {
|
||||
char *s = strrchr (string, ']');
|
||||
if ( s == NULL ) s = strrchr (string, ':');
|
||||
if ( s != NULL ) strcpy (string, s+1);
|
||||
}
|
||||
/*
|
||||
* Be consistent with what the C RTL has already done to the rest of
|
||||
* the argv items and lowercase all of these names.
|
||||
*/
|
||||
for (c = string; *c; ++c)
|
||||
if (isupper (*c))
|
||||
*c = tolower (*c);
|
||||
add_item (head, tail, string, count);
|
||||
++expcount;
|
||||
}
|
||||
if (expcount == 0)
|
||||
add_item (head, tail, item, count);
|
||||
lib$sfree1_dd (&resultspec);
|
||||
lib$find_file_end (&context);
|
||||
}
|
||||
|
||||
static int child_st [2]; /* Event Flag set when child process completes */
|
||||
|
||||
static short child_chan;/* I/O Channel for Pipe Mailbox */
|
||||
|
||||
static exit_handler (status)
|
||||
int *status;
|
||||
{
|
||||
short iosb [4];
|
||||
|
||||
if (0 == child_st [0])
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "Waiting for Child Process to Finnish . . .\n");
|
||||
#endif
|
||||
sys$qiow (0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
sys$dassgn (child_chan);
|
||||
fclose (stdout);
|
||||
sys$synch (0, child_st);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static sig_child (chan)
|
||||
int chan;
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "Child Completion AST\n");
|
||||
#endif
|
||||
if (child_st [0] == 0)
|
||||
child_st [0] = 1;
|
||||
}
|
||||
|
||||
static struct exit_control_block
|
||||
{
|
||||
struct exit_control_block *flink;
|
||||
int (*exit_routine) ();
|
||||
int arg_count;
|
||||
int *status_address;
|
||||
int exit_status;
|
||||
} exit_block =
|
||||
{
|
||||
0,
|
||||
exit_handler,
|
||||
1,
|
||||
&exit_block.exit_status,
|
||||
0
|
||||
};
|
||||
|
||||
static char *pipe_and_fork (cmd)
|
||||
char *cmd;
|
||||
{
|
||||
$DESCRIPTOR (cmddsc, cmd);
|
||||
static char mbxname [64];
|
||||
$DESCRIPTOR (mbxdsc, mbxname);
|
||||
short iosb [4];
|
||||
int status;
|
||||
int pid;
|
||||
struct
|
||||
{
|
||||
short dna_buflen;
|
||||
short dna_itmcod;
|
||||
char *dna_buffer;
|
||||
unsigned short *dna_retlen;
|
||||
int listend;
|
||||
} itmlst =
|
||||
{
|
||||
sizeof (mbxname),
|
||||
DVI$_DEVNAM,
|
||||
mbxname,
|
||||
&mbxdsc.dsc$w_length,
|
||||
0
|
||||
};
|
||||
int mbxsize;
|
||||
struct
|
||||
{
|
||||
short mbf_buflen;
|
||||
short mbf_itmcod;
|
||||
int *mbf_maxbuf;
|
||||
unsigned short *mbf_retlen;
|
||||
int listend;
|
||||
} syiitmlst =
|
||||
{
|
||||
sizeof (mbxsize),
|
||||
SYI$_MAXBUF,
|
||||
&mbxsize,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
cmddsc.dsc$w_length = strlen (cmd);
|
||||
/*
|
||||
* Get the SYSGEN parameter MAXBUF, and the smaller of it and 2048 as
|
||||
* the size of the 'pipe' mailbox.
|
||||
*/
|
||||
if (1 == (1& (vaxc$errno = sys$getsyiw (0, 0, 0, &syiitmlst, iosb, 0, 0, 0))))
|
||||
vaxc$errno = iosb [0];
|
||||
if (0 == (1&vaxc$errno))
|
||||
{
|
||||
errno = EVMSERR;
|
||||
perror ("Can't get SYSGEN parameter value for MAXBUF");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
if (mbxsize > 2048)
|
||||
mbxsize = 2048;
|
||||
if (0 == (1& (vaxc$errno = sys$crembx (0, &child_chan, mbxsize, mbxsize, 0, 0, 0))))
|
||||
{
|
||||
errno = EVMSERR;
|
||||
perror ("Can't create pipe mailbox");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
if (1 == (1& (vaxc$errno = sys$getdviw (0, child_chan, 0, &itmlst, iosb,
|
||||
0, 0, 0))))
|
||||
vaxc$errno = iosb [0];
|
||||
if (0 == (1&vaxc$errno))
|
||||
{
|
||||
errno = EVMSERR;
|
||||
perror ("Can't get pipe mailbox device name");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
mbxname [mbxdsc.dsc$w_length] = '\0';
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "Pipe Mailbox Name = '%s'\n", mbxname);
|
||||
#endif
|
||||
if (0 == (1& (vaxc$errno = lib$spawn (&cmddsc, &mbxdsc, 0, &1,
|
||||
0, &pid, child_st, &0, sig_child,
|
||||
&child_chan))))
|
||||
{
|
||||
errno = EVMSERR;
|
||||
perror ("Can't spawn subprocess");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "Subprocess's Pid = %08X\n", pid);
|
||||
#endif
|
||||
sys$dclexh (&exit_block);
|
||||
return (mbxname);
|
||||
}
|
||||
|
||||
background_process (argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
char command [2048] = "$";
|
||||
$DESCRIPTOR (value, command);
|
||||
$DESCRIPTOR (cmd, "BACKGROUND$COMMAND");
|
||||
$DESCRIPTOR (null, "NLA0:");
|
||||
int pid;
|
||||
|
||||
strcat (command, argv [0]);
|
||||
while (--argc)
|
||||
{
|
||||
strcat (command, " \"");
|
||||
strcat (command, *(++argv));
|
||||
strcat (command, "\"");
|
||||
}
|
||||
value.dsc$w_length = strlen (command);
|
||||
if (0 == (1& (vaxc$errno = lib$set_symbol (&cmd, &value))))
|
||||
{
|
||||
errno = EVMSERR;
|
||||
perror ("Can't create symbol for subprocess command");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
if (0 == (1& (vaxc$errno = lib$spawn (&cmd, &null, 0, &17, 0, &pid))))
|
||||
{
|
||||
errno = EVMSERR;
|
||||
perror ("Can't spawn subprocess");
|
||||
exit (EXIT_ERR);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "%s\n", command);
|
||||
#endif
|
||||
fprintf (stderr, "%08X\n", pid);
|
||||
return (EXIT_OK);
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* $Id: args.c,v 1.7 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for reading command line arguments.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "args.h"
|
||||
#include "debug.h"
|
||||
#include "routines.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static char *nextStringArg (const char** const next)
|
||||
{
|
||||
char* result = NULL;
|
||||
const char* start;
|
||||
|
||||
Assert (*next != NULL);
|
||||
for (start = *next ; isspace ((int) *start) ; ++start)
|
||||
;
|
||||
if (*start == '\0')
|
||||
*next = start;
|
||||
else
|
||||
{
|
||||
size_t length;
|
||||
const char* end;
|
||||
|
||||
for (end = start ; *end != '\0' && ! isspace ((int) *end) ; ++end)
|
||||
;
|
||||
length = end - start;
|
||||
Assert (length > 0);
|
||||
result = xMalloc (length + 1, char);
|
||||
strncpy (result, start, length);
|
||||
result [length] = '\0';
|
||||
*next = end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* nextStringLine (const char** const next)
|
||||
{
|
||||
char* result = NULL;
|
||||
size_t length;
|
||||
const char* end;
|
||||
|
||||
Assert (*next != NULL);
|
||||
for (end = *next ; *end != '\n' && *end != '\0' ; ++end)
|
||||
;
|
||||
length = end - *next;
|
||||
if (length > 0)
|
||||
{
|
||||
result = xMalloc (length + 1, char);
|
||||
strncpy (result, *next, length);
|
||||
result [length] = '\0';
|
||||
}
|
||||
if (*end == '\n')
|
||||
++end;
|
||||
else if (*end == '\r')
|
||||
{
|
||||
++end;
|
||||
if (*end == '\n')
|
||||
++end;
|
||||
}
|
||||
*next = end;
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* nextString (const Arguments* const current, const char** const next)
|
||||
{
|
||||
char* result;
|
||||
if (current->lineMode)
|
||||
result = nextStringLine (next);
|
||||
else
|
||||
result = nextStringArg (next);
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* nextFileArg (FILE* const fp)
|
||||
{
|
||||
char* result = NULL;
|
||||
Assert (fp != NULL);
|
||||
if (! feof (fp))
|
||||
{
|
||||
vString* vs = vStringNew ();
|
||||
int c;
|
||||
do
|
||||
c = fgetc (fp);
|
||||
while (isspace (c));
|
||||
|
||||
if (c != EOF)
|
||||
{
|
||||
do
|
||||
{
|
||||
vStringPut (vs, c);
|
||||
c = fgetc (fp);
|
||||
} while (c != EOF && ! isspace (c));
|
||||
vStringTerminate (vs);
|
||||
Assert (vStringLength (vs) > 0);
|
||||
result = xMalloc (vStringLength (vs) + 1, char);
|
||||
strcpy (result, vStringValue (vs));
|
||||
}
|
||||
vStringDelete (vs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* nextFileLine (FILE* const fp)
|
||||
{
|
||||
char* result = NULL;
|
||||
if (! feof (fp))
|
||||
{
|
||||
vString* vs = vStringNew ();
|
||||
int c;
|
||||
|
||||
Assert (fp != NULL);
|
||||
c = fgetc (fp);
|
||||
while (c != EOF)
|
||||
{
|
||||
if (c != '\n' && c != '\r')
|
||||
vStringPut (vs, c);
|
||||
else if (vStringLength (vs) > 0)
|
||||
break;
|
||||
c = fgetc (fp);
|
||||
}
|
||||
if (c != EOF || vStringLength (vs) > 0)
|
||||
{
|
||||
if (c == '\r')
|
||||
{
|
||||
c = fgetc (fp);
|
||||
if (c != '\n')
|
||||
c = ungetc (c, fp);
|
||||
}
|
||||
vStringTerminate (vs);
|
||||
result = xMalloc (vStringLength (vs) + 1, char);
|
||||
strcpy (result, vStringValue (vs));
|
||||
vStringDelete (vs);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* nextFileString (const Arguments* const current, FILE* const fp)
|
||||
{
|
||||
char* result;
|
||||
if (current->lineMode)
|
||||
result = nextFileLine (fp);
|
||||
else
|
||||
result = nextFileArg (fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern Arguments* argNewFromString (const char* const string)
|
||||
{
|
||||
Arguments* result = xMalloc (1, Arguments);
|
||||
memset (result, 0, sizeof (Arguments));
|
||||
result->type = ARG_STRING;
|
||||
result->u.stringArgs.string = string;
|
||||
result->u.stringArgs.item = string;
|
||||
result->u.stringArgs.next = string;
|
||||
result->item = nextString (result, &result->u.stringArgs.next);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern Arguments* argNewFromArgv (char* const* const argv)
|
||||
{
|
||||
Arguments* result = xMalloc (1, Arguments);
|
||||
memset (result, 0, sizeof (Arguments));
|
||||
result->type = ARG_ARGV;
|
||||
result->u.argvArgs.argv = argv;
|
||||
result->u.argvArgs.item = result->u.argvArgs.argv;
|
||||
result->item = *result->u.argvArgs.item;
|
||||
return result;
|
||||
}
|
||||
|
||||
extern Arguments* argNewFromFile (FILE* const fp)
|
||||
{
|
||||
Arguments* result = xMalloc (1, Arguments);
|
||||
memset (result, 0, sizeof (Arguments));
|
||||
result->type = ARG_FILE;
|
||||
result->u.fileArgs.fp = fp;
|
||||
result->item = nextFileString (result, result->u.fileArgs.fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern Arguments* argNewFromLineFile (FILE* const fp)
|
||||
{
|
||||
Arguments* result = xMalloc (1, Arguments);
|
||||
memset (result, 0, sizeof (Arguments));
|
||||
result->type = ARG_FILE;
|
||||
result->lineMode = TRUE;
|
||||
result->u.fileArgs.fp = fp;
|
||||
result->item = nextFileString (result, result->u.fileArgs.fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern char *argItem (const Arguments* const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
Assert (! argOff (current));
|
||||
return current->item;
|
||||
}
|
||||
|
||||
extern boolean argOff (const Arguments* const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
return (boolean) (current->item == NULL);
|
||||
}
|
||||
|
||||
extern void argSetWordMode (Arguments* const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
current->lineMode = FALSE;
|
||||
}
|
||||
|
||||
extern void argSetLineMode (Arguments* const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
current->lineMode = TRUE;
|
||||
}
|
||||
|
||||
extern void argForth (Arguments* const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
Assert (! argOff (current));
|
||||
switch (current->type)
|
||||
{
|
||||
case ARG_STRING:
|
||||
if (current->item != NULL)
|
||||
eFree (current->item);
|
||||
current->u.stringArgs.item = current->u.stringArgs.next;
|
||||
current->item = nextString (current, ¤t->u.stringArgs.next);
|
||||
break;
|
||||
case ARG_ARGV:
|
||||
++current->u.argvArgs.item;
|
||||
current->item = *current->u.argvArgs.item;
|
||||
break;
|
||||
case ARG_FILE:
|
||||
if (current->item != NULL)
|
||||
eFree (current->item);
|
||||
current->item = nextFileString (current, current->u.fileArgs.fp);
|
||||
break;
|
||||
default:
|
||||
Assert ("Invalid argument type" == NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern void argDelete (Arguments* const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
if (current->type == ARG_STRING && current->item != NULL)
|
||||
eFree (current->item);
|
||||
memset (current, 0, sizeof (Arguments));
|
||||
eFree (current);
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* $Id: args.h,v 1.5 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Defines external interface to command line argument reading.
|
||||
*/
|
||||
#ifndef _ARGS_H
|
||||
#define _ARGS_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
typedef enum { ARG_NONE, ARG_STRING, ARG_ARGV, ARG_FILE } argType;
|
||||
|
||||
typedef struct sArgs {
|
||||
argType type;
|
||||
union {
|
||||
struct sStringArgs {
|
||||
const char* string;
|
||||
const char* next;
|
||||
const char* item;
|
||||
} stringArgs;
|
||||
struct sArgvArgs {
|
||||
char* const* argv;
|
||||
char* const* item;
|
||||
} argvArgs;
|
||||
struct sFileArgs {
|
||||
FILE* fp;
|
||||
} fileArgs;
|
||||
} u;
|
||||
char* item;
|
||||
boolean lineMode;
|
||||
} Arguments;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern Arguments* argNewFromString (const char* const string);
|
||||
extern Arguments* argNewFromArgv (char* const* const argv);
|
||||
extern Arguments* argNewFromFile (FILE* const fp);
|
||||
extern Arguments* argNewFromLineFile (FILE* const fp);
|
||||
extern char *argItem (const Arguments* const current);
|
||||
extern boolean argOff (const Arguments* const current);
|
||||
extern void argSetWordMode (Arguments* const current);
|
||||
extern void argSetLineMode (Arguments* const current);
|
||||
extern void argForth (Arguments* const current);
|
||||
extern void argDelete (Arguments* const current);
|
||||
|
||||
#endif /* _ARGS_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* $Id: asm.c,v 1.18 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for assembly language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "keyword.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_NONE = -1, K_DEFINE, K_LABEL, K_MACRO, K_TYPE
|
||||
} AsmKind;
|
||||
|
||||
typedef enum {
|
||||
OP_UNDEFINED = -1,
|
||||
OP_ALIGN,
|
||||
OP_COLON_EQUAL,
|
||||
OP_END,
|
||||
OP_ENDM,
|
||||
OP_ENDMACRO,
|
||||
OP_ENDP,
|
||||
OP_ENDS,
|
||||
OP_EQU,
|
||||
OP_EQUAL,
|
||||
OP_LABEL,
|
||||
OP_MACRO,
|
||||
OP_PROC,
|
||||
OP_RECORD,
|
||||
OP_SECTIONS,
|
||||
OP_SET,
|
||||
OP_STRUCT,
|
||||
OP_LAST
|
||||
} opKeyword;
|
||||
|
||||
typedef struct {
|
||||
const char *operator;
|
||||
opKeyword keyword;
|
||||
} asmKeyword;
|
||||
|
||||
typedef struct {
|
||||
opKeyword keyword;
|
||||
AsmKind kind;
|
||||
} opKind;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static langType Lang_asm;
|
||||
|
||||
static kindOption AsmKinds [] = {
|
||||
{ TRUE, 'd', "define", "defines" },
|
||||
{ TRUE, 'l', "label", "labels" },
|
||||
{ TRUE, 'm', "macro", "macros" },
|
||||
{ TRUE, 't', "type", "types (structs and records)" }
|
||||
};
|
||||
|
||||
static const asmKeyword AsmKeywords [] = {
|
||||
{ "align", OP_ALIGN },
|
||||
{ "endmacro", OP_ENDMACRO },
|
||||
{ "endm", OP_ENDM },
|
||||
{ "end", OP_END },
|
||||
{ "endp", OP_ENDP },
|
||||
{ "ends", OP_ENDS },
|
||||
{ "equ", OP_EQU },
|
||||
{ "label", OP_LABEL },
|
||||
{ "macro", OP_MACRO },
|
||||
{ ":=", OP_COLON_EQUAL },
|
||||
{ "=", OP_EQUAL },
|
||||
{ "proc", OP_PROC },
|
||||
{ "record", OP_RECORD },
|
||||
{ "sections", OP_SECTIONS },
|
||||
{ "set", OP_SET },
|
||||
{ "struct", OP_STRUCT }
|
||||
};
|
||||
|
||||
static const opKind OpKinds [] = {
|
||||
/* must be ordered same as opKeyword enumeration */
|
||||
{ OP_ALIGN, K_NONE },
|
||||
{ OP_COLON_EQUAL, K_DEFINE },
|
||||
{ OP_END, K_NONE },
|
||||
{ OP_ENDM, K_NONE },
|
||||
{ OP_ENDMACRO, K_NONE },
|
||||
{ OP_ENDP, K_NONE },
|
||||
{ OP_ENDS, K_NONE },
|
||||
{ OP_EQU, K_DEFINE },
|
||||
{ OP_EQUAL, K_DEFINE },
|
||||
{ OP_LABEL, K_LABEL },
|
||||
{ OP_MACRO, K_MACRO },
|
||||
{ OP_PROC, K_LABEL },
|
||||
{ OP_RECORD, K_TYPE },
|
||||
{ OP_SECTIONS, K_NONE },
|
||||
{ OP_SET, K_DEFINE },
|
||||
{ OP_STRUCT, K_TYPE }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
static void buildAsmKeywordHash (void)
|
||||
{
|
||||
const size_t count = sizeof (AsmKeywords) / sizeof (AsmKeywords [0]);
|
||||
size_t i;
|
||||
for (i = 0 ; i < count ; ++i)
|
||||
{
|
||||
const asmKeyword* const p = AsmKeywords + i;
|
||||
addKeyword (p->operator, Lang_asm, (int) p->keyword);
|
||||
}
|
||||
}
|
||||
|
||||
static opKeyword analyzeOperator (const vString *const op)
|
||||
{
|
||||
static vString *keyword = NULL;
|
||||
opKeyword result = OP_UNDEFINED;
|
||||
|
||||
if (keyword == NULL)
|
||||
keyword = vStringNew ();
|
||||
vStringCopyToLower (keyword, op);
|
||||
result = (opKeyword) lookupKeyword (vStringValue (keyword), Lang_asm);
|
||||
return result;
|
||||
}
|
||||
|
||||
static boolean isInitialSymbolCharacter (int c)
|
||||
{
|
||||
return (boolean) (c != '\0' && (isalpha (c) || strchr ("_$", c) != NULL));
|
||||
}
|
||||
|
||||
static boolean isSymbolCharacter (int c)
|
||||
{
|
||||
/* '?' character is allowed in AMD 29K family */
|
||||
return (boolean) (c != '\0' && (isalnum (c) || strchr ("_$?", c) != NULL));
|
||||
}
|
||||
|
||||
static boolean readPreProc (const unsigned char *const line)
|
||||
{
|
||||
boolean result;
|
||||
const unsigned char *cp = line;
|
||||
vString *name = vStringNew ();
|
||||
while (isSymbolCharacter ((int) *cp))
|
||||
{
|
||||
vStringPut (name, *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
result = (boolean) (strcmp (vStringValue (name), "define") == 0);
|
||||
if (result)
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
vStringClear (name);
|
||||
while (isSymbolCharacter ((int) *cp))
|
||||
{
|
||||
vStringPut (name, *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AsmKinds, K_DEFINE);
|
||||
}
|
||||
vStringDelete (name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static AsmKind operatorKind (
|
||||
const vString *const operator,
|
||||
boolean *const found)
|
||||
{
|
||||
AsmKind result = K_NONE;
|
||||
const opKeyword kw = analyzeOperator (operator);
|
||||
*found = (boolean) (kw != OP_UNDEFINED);
|
||||
if (*found)
|
||||
{
|
||||
result = OpKinds [kw].kind;
|
||||
Assert (OpKinds [kw].keyword == kw);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* We must check for "DB", "DB.L", "DCB.W" (68000)
|
||||
*/
|
||||
static boolean isDefineOperator (const vString *const operator)
|
||||
{
|
||||
const unsigned char *const op =
|
||||
(unsigned char*) vStringValue (operator);
|
||||
const size_t length = vStringLength (operator);
|
||||
const boolean result = (boolean) (length > 0 &&
|
||||
toupper ((int) *op) == 'D' &&
|
||||
(length == 2 ||
|
||||
(length == 4 && (int) op [2] == '.') ||
|
||||
(length == 5 && (int) op [3] == '.')));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void makeAsmTag (
|
||||
const vString *const name,
|
||||
const vString *const operator,
|
||||
const boolean labelCandidate,
|
||||
const boolean nameFollows)
|
||||
{
|
||||
if (vStringLength (name) > 0)
|
||||
{
|
||||
boolean found;
|
||||
const AsmKind kind = operatorKind (operator, &found);
|
||||
if (found)
|
||||
{
|
||||
if (kind != K_NONE)
|
||||
makeSimpleTag (name, AsmKinds, kind);
|
||||
}
|
||||
else if (isDefineOperator (operator))
|
||||
{
|
||||
if (! nameFollows)
|
||||
makeSimpleTag (name, AsmKinds, K_DEFINE);
|
||||
}
|
||||
else if (labelCandidate)
|
||||
{
|
||||
operatorKind (name, &found);
|
||||
if (! found)
|
||||
makeSimpleTag (name, AsmKinds, K_LABEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char *readSymbol (
|
||||
const unsigned char *const start,
|
||||
vString *const sym)
|
||||
{
|
||||
const unsigned char *cp = start;
|
||||
vStringClear (sym);
|
||||
if (isInitialSymbolCharacter ((int) *cp))
|
||||
{
|
||||
while (isSymbolCharacter ((int) *cp))
|
||||
{
|
||||
vStringPut (sym, *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (sym);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
static const unsigned char *readOperator (
|
||||
const unsigned char *const start,
|
||||
vString *const operator)
|
||||
{
|
||||
const unsigned char *cp = start;
|
||||
vStringClear (operator);
|
||||
while (*cp != '\0' && ! isspace ((int) *cp))
|
||||
{
|
||||
vStringPut (operator, *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (operator);
|
||||
return cp;
|
||||
}
|
||||
|
||||
static void findAsmTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
vString *operator = vStringNew ();
|
||||
const unsigned char *line;
|
||||
boolean inCComment = FALSE;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
boolean labelCandidate = (boolean) (! isspace ((int) *cp));
|
||||
boolean nameFollows = FALSE;
|
||||
const boolean isComment = (boolean)
|
||||
(*cp != '\0' && strchr (";*@", *cp) != NULL);
|
||||
|
||||
/* skip comments */
|
||||
if (strncmp ((const char*) cp, "/*", (size_t) 2) == 0)
|
||||
{
|
||||
inCComment = TRUE;
|
||||
cp += 2;
|
||||
}
|
||||
if (inCComment)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (strncmp ((const char*) cp, "*/", (size_t) 2) == 0)
|
||||
{
|
||||
inCComment = FALSE;
|
||||
cp += 2;
|
||||
break;
|
||||
}
|
||||
++cp;
|
||||
} while (*cp != '\0');
|
||||
}
|
||||
if (isComment || inCComment)
|
||||
continue;
|
||||
|
||||
/* read preprocessor defines */
|
||||
if (*cp == '#')
|
||||
{
|
||||
++cp;
|
||||
readPreProc (cp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* skip white space */
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
/* read symbol */
|
||||
cp = readSymbol (cp, name);
|
||||
if (vStringLength (name) > 0 && *cp == ':')
|
||||
{
|
||||
labelCandidate = TRUE;
|
||||
++cp;
|
||||
}
|
||||
|
||||
if (! isspace ((int) *cp) && *cp != '\0')
|
||||
continue;
|
||||
|
||||
/* skip white space */
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
/* skip leading dot */
|
||||
#if 0
|
||||
if (*cp == '.')
|
||||
++cp;
|
||||
#endif
|
||||
|
||||
cp = readOperator (cp, operator);
|
||||
|
||||
/* attempt second read of symbol */
|
||||
if (vStringLength (name) == 0)
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
cp = readSymbol (cp, name);
|
||||
nameFollows = TRUE;
|
||||
}
|
||||
makeAsmTag (name, operator, labelCandidate, nameFollows);
|
||||
}
|
||||
vStringDelete (name);
|
||||
vStringDelete (operator);
|
||||
}
|
||||
|
||||
static void initialize (const langType language)
|
||||
{
|
||||
Lang_asm = language;
|
||||
buildAsmKeywordHash ();
|
||||
}
|
||||
|
||||
extern parserDefinition* AsmParser (void)
|
||||
{
|
||||
static const char *const extensions [] = {
|
||||
"asm", "ASM", "s", "S", NULL
|
||||
};
|
||||
static const char *const patterns [] = {
|
||||
"*.A51",
|
||||
"*.29[kK]",
|
||||
"*.[68][68][kKsSxX]",
|
||||
"*.[xX][68][68]",
|
||||
NULL
|
||||
};
|
||||
parserDefinition* def = parserNew ("Asm");
|
||||
def->kinds = AsmKinds;
|
||||
def->kindCount = KIND_COUNT (AsmKinds);
|
||||
def->extensions = extensions;
|
||||
def->patterns = patterns;
|
||||
def->parser = findAsmTags;
|
||||
def->initialize = initialize;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* $Id: asp.c,v 1.8 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000, Patrick Dehne <[email protected]>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for the ASP (Active
|
||||
* Server Pages) web page scripting language.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CONST, K_FUNCTION, K_SUB, K_DIM
|
||||
} aspKind;
|
||||
|
||||
static kindOption AspKinds [] = {
|
||||
{ TRUE, 'c', "constant", "constants"},
|
||||
{ TRUE, 'f', "function", "functions"},
|
||||
{ TRUE, 's', "subroutine", "subroutines"},
|
||||
{ TRUE, 'v', "variable", "variables"}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void findAspTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
|
||||
while (*cp != '\0')
|
||||
{
|
||||
/* jump over whitespace */
|
||||
while (isspace ((int)*cp))
|
||||
cp++;
|
||||
|
||||
/* jump over strings */
|
||||
if (*cp == '"')
|
||||
{
|
||||
cp++;
|
||||
while (*cp!='"' && *cp!='\0')
|
||||
cp++;
|
||||
}
|
||||
|
||||
/* jump over comments */
|
||||
else if (*cp == '\'')
|
||||
break;
|
||||
|
||||
/* jump over end function/sub lines */
|
||||
else if (strncasecmp ((const char*) cp, "end", (size_t) 3)== 0)
|
||||
{
|
||||
cp += 3;
|
||||
if (isspace ((int)*cp))
|
||||
{
|
||||
while (isspace ((int)*cp))
|
||||
++cp;
|
||||
|
||||
if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
|
||||
{
|
||||
cp+=8;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
|
||||
{
|
||||
cp+=3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* jump over exit function/sub lines */
|
||||
else if (strncasecmp ((const char*) cp, "exit", (size_t) 4)==0)
|
||||
{
|
||||
cp += 4;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
|
||||
{
|
||||
cp+=8;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
|
||||
{
|
||||
cp+=3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* function? */
|
||||
else if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
|
||||
{
|
||||
cp += 8;
|
||||
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
|
||||
/* sub? */
|
||||
else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
|
||||
{
|
||||
cp += 3;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_SUB);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
|
||||
/* dim variable? */
|
||||
else if (strncasecmp ((const char*) cp, "dim", (size_t) 3) == 0)
|
||||
{
|
||||
cp += 3;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_DIM);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
|
||||
/* const declaration? */
|
||||
else if (strncasecmp ((const char*) cp, "const", (size_t) 5) == 0)
|
||||
{
|
||||
cp += 5;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_CONST);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
|
||||
/* nothing relevant */
|
||||
else if (*cp != '\0')
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* AspParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "asp", "asa", NULL };
|
||||
parserDefinition* def = parserNew ("Asp");
|
||||
def->kinds = AspKinds;
|
||||
def->kindCount = KIND_COUNT (AspKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findAspTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* $Id: awk.c,v 1.3 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for AWK functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum eAwkKinds {
|
||||
K_FUNCTION
|
||||
} awkKind;
|
||||
|
||||
static kindOption AwkKinds [] = {
|
||||
{ TRUE, 'f', "function", "functions" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void findAwkTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
if (strncmp ((const char*) line, "function", (size_t) 8) == 0 &&
|
||||
isspace ((int) line [8]))
|
||||
{
|
||||
const unsigned char *cp = line + 8;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (*cp == '(')
|
||||
makeSimpleTag (name, AwkKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
if (*cp != '\0')
|
||||
++cp;
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* AwkParser ()
|
||||
{
|
||||
static const char *const extensions [] = { "awk", "gawk", "mawk", NULL };
|
||||
parserDefinition* def = parserNew ("Awk");
|
||||
def->kinds = AwkKinds;
|
||||
def->kindCount = KIND_COUNT (AwkKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findAwkTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* $Id: beta.c,v 1.7 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2000, Mjølner Informatics
|
||||
*
|
||||
* Written by Erik Corry <[email protected]>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for BETA language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "entry.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define isbident(c) (identarray [(unsigned char) (c)])
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_FRAGMENT, K_PATTERN, K_SLOT, K_VIRTUAL
|
||||
} betaKind;
|
||||
|
||||
static kindOption BetaKinds [] = {
|
||||
{ TRUE, 'f', "fragment", "fragment definitions"},
|
||||
{ FALSE, 'p', "pattern", "all patterns"},
|
||||
{ TRUE, 's', "slot", "slots (fragment uses)"},
|
||||
{ TRUE, 'v', "virtual", "patterns (virtual or rebound)"}
|
||||
};
|
||||
|
||||
/* [A-Z_a-z0-9] */
|
||||
static const char identarray [256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32-47 !"#$%&'()*+'-./ */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 0123456789:;<=>? */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 @ABCDEFGHIJKLMNO */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 PQRSTUVWXYZ [\]^_ */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 `abcdefghijklmno */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 pqrstuvwxyz{|}~ */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128- */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* -255 */
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void makeBetaTag (const char* const name, const betaKind kind)
|
||||
{
|
||||
if (BetaKinds [kind].enabled)
|
||||
{
|
||||
tagEntryInfo e;
|
||||
initTagEntry (&e, name);
|
||||
e.kindName = BetaKinds [kind].name;
|
||||
e.kind = BetaKinds [kind].letter;
|
||||
makeTagEntry (&e);
|
||||
}
|
||||
}
|
||||
|
||||
static void findBetaTags (void)
|
||||
{
|
||||
vString *line = vStringNew ();
|
||||
boolean incomment = FALSE;
|
||||
boolean inquote = FALSE;
|
||||
boolean dovirtuals = BetaKinds [K_VIRTUAL].enabled;
|
||||
boolean dopatterns = BetaKinds [K_PATTERN].enabled;
|
||||
|
||||
do
|
||||
{
|
||||
boolean foundfragmenthere = FALSE;
|
||||
/* find fragment definition (line that starts and ends with --) */
|
||||
int last;
|
||||
int first;
|
||||
int c;
|
||||
|
||||
vStringClear (line);
|
||||
|
||||
while ((c = fileGetc ()) != EOF && c != '\n' && c != '\r')
|
||||
vStringPut (line, c);
|
||||
|
||||
vStringTerminate (line);
|
||||
|
||||
last = vStringLength (line) - 1;
|
||||
first = 0;
|
||||
/* skip white space at start and end of line */
|
||||
while (last && isspace ((int) vStringChar (line, last))) last--;
|
||||
while (first < last && isspace ((int) vStringChar (line, first))) first++;
|
||||
/* if line still has a reasonable length and ... */
|
||||
if (last - first > 4 &&
|
||||
(vStringChar (line, first) == '-' &&
|
||||
vStringChar (line, first + 1) == '-' &&
|
||||
vStringChar (line, last) == '-' &&
|
||||
vStringChar (line, last - 1) == '-'))
|
||||
{
|
||||
if (!incomment && !inquote)
|
||||
{
|
||||
foundfragmenthere = TRUE;
|
||||
/* skip past -- and whitespace. Also skip back past 'dopart'
|
||||
or 'attributes' to the :. We have to do this because there
|
||||
is no sensible way to include whitespace in a ctags token
|
||||
so the conventional space after the ':' would mess us up */
|
||||
last -= 2;
|
||||
first += 2;
|
||||
while (last && vStringChar (line, last) != ':') last--;
|
||||
while (last && (isspace ((int) vStringChar (line, last-1)))) last--;
|
||||
while (first < last &&
|
||||
(isspace ((int) vStringChar (line, first)) ||
|
||||
vStringChar (line, first) == '-'))
|
||||
first++;
|
||||
/* If there's anything left it is a fragment title */
|
||||
if (first < last - 1)
|
||||
{
|
||||
vStringChar (line, last) = 0;
|
||||
if (strcasecmp ("LIB", vStringValue (line) + first) &&
|
||||
strcasecmp ("PROGRAM", vStringValue (line) + first))
|
||||
{
|
||||
makeBetaTag (vStringValue (line) + first, K_FRAGMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int pos = 0;
|
||||
int len = vStringLength (line);
|
||||
if (inquote) goto stringtext;
|
||||
if (incomment) goto commenttext;
|
||||
programtext:
|
||||
for ( ; pos < len; pos++)
|
||||
{
|
||||
if (vStringChar (line, pos) == '\'')
|
||||
{
|
||||
pos++;
|
||||
inquote = TRUE;
|
||||
goto stringtext;
|
||||
}
|
||||
if (vStringChar (line, pos) == '{')
|
||||
{
|
||||
pos++;
|
||||
incomment = TRUE;
|
||||
goto commenttext;
|
||||
}
|
||||
if (vStringChar (line, pos) == '(' && pos < len - 1 &&
|
||||
vStringChar (line, pos+1) == '*')
|
||||
{
|
||||
pos +=2;
|
||||
incomment = TRUE;
|
||||
goto commenttext;
|
||||
}
|
||||
/*
|
||||
* SLOT definition looks like this:
|
||||
* <<SLOT nameofslot: dopart>>
|
||||
* or
|
||||
* <<SLOT nameofslot: descriptor>>
|
||||
*/
|
||||
if (!foundfragmenthere &&
|
||||
vStringChar (line, pos) == '<' &&
|
||||
pos+1 < len &&
|
||||
vStringChar (line, pos+1) == '<' &&
|
||||
strstr (vStringValue (line) + pos, ">>"))
|
||||
{
|
||||
/* Found slot name, get start and end */
|
||||
int eoname;
|
||||
char c2;
|
||||
pos += 2; /* skip past << */
|
||||
/* skip past space before SLOT */
|
||||
while (pos < len && isspace ((int) vStringChar (line, pos)))
|
||||
pos++;
|
||||
/* skip past SLOT */
|
||||
if (pos+4 <= len &&
|
||||
!strncasecmp (vStringValue(line) + pos, "SLOT", (size_t)4))
|
||||
pos += 4;
|
||||
/* skip past space after SLOT */
|
||||
while (pos < len && isspace ((int) vStringChar (line, pos)))
|
||||
pos++;
|
||||
eoname = pos;
|
||||
/* skip to end of name */
|
||||
while (eoname < len &&
|
||||
(c2 = vStringChar (line, eoname)) != '>' &&
|
||||
c2 != ':' &&
|
||||
!isspace ((int) c2))
|
||||
eoname++;
|
||||
if (eoname < len)
|
||||
{
|
||||
vStringChar (line, eoname) = 0;
|
||||
if (strcasecmp ("LIB", vStringValue (line) + pos) &&
|
||||
strcasecmp ("PROGRAM", vStringValue (line) + pos) &&
|
||||
strcasecmp ("SLOT", vStringValue (line) + pos))
|
||||
{
|
||||
makeBetaTag (vStringValue (line) + pos, K_SLOT);
|
||||
}
|
||||
}
|
||||
if (eoname+1 < len) {
|
||||
pos = eoname + 1;
|
||||
} else {
|
||||
pos = len;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Only patterns that are virtual, extensions of virtuals or
|
||||
* final bindings are normally included so as not to overload
|
||||
* totally.
|
||||
* That means one of the forms name:: name:< or name::<
|
||||
*/
|
||||
if (!foundfragmenthere &&
|
||||
vStringChar (line, pos) == ':' &&
|
||||
(dopatterns ||
|
||||
(dovirtuals &&
|
||||
(vStringChar (line, pos+1) == ':' ||
|
||||
vStringChar (line, pos+1) == '<')
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
/* Found pattern name, get start and end */
|
||||
int eoname = pos;
|
||||
int soname;
|
||||
while (eoname && isspace ((int) vStringChar (line, eoname-1)))
|
||||
eoname--;
|
||||
foundanothername:
|
||||
/* terminate right after name */
|
||||
vStringChar (line, eoname) = 0;
|
||||
soname = eoname;
|
||||
while (soname &&
|
||||
isbident (vStringChar (line, soname-1)))
|
||||
{
|
||||
soname--;
|
||||
}
|
||||
if (soname != eoname)
|
||||
{
|
||||
makeBetaTag (vStringValue (line) + soname, K_PATTERN);
|
||||
/* scan back past white space */
|
||||
while (soname &&
|
||||
isspace ((int) vStringChar (line, soname-1)))
|
||||
soname--;
|
||||
if (soname && vStringChar (line, soname-1) == ',')
|
||||
{
|
||||
/* we found a new pattern name before comma */
|
||||
eoname = soname;
|
||||
goto foundanothername;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
goto endofline;
|
||||
commenttext:
|
||||
for ( ; pos < len; pos++)
|
||||
{
|
||||
if (vStringChar (line, pos) == '*' && pos < len - 1 &&
|
||||
vStringChar (line, pos+1) == ')')
|
||||
{
|
||||
pos += 2;
|
||||
incomment = FALSE;
|
||||
goto programtext;
|
||||
}
|
||||
if (vStringChar (line, pos) == '}')
|
||||
{
|
||||
pos++;
|
||||
incomment = FALSE;
|
||||
goto programtext;
|
||||
}
|
||||
}
|
||||
goto endofline;
|
||||
stringtext:
|
||||
for ( ; pos < len; pos++)
|
||||
{
|
||||
if (vStringChar (line, pos) == '\\')
|
||||
{
|
||||
if (pos < len - 1) pos++;
|
||||
}
|
||||
else if (vStringChar (line, pos) == '\'')
|
||||
{
|
||||
pos++;
|
||||
/* support obsolete '' syntax */
|
||||
if (pos < len && vStringChar (line, pos) == '\'')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
inquote = FALSE;
|
||||
goto programtext;
|
||||
}
|
||||
}
|
||||
}
|
||||
endofline:
|
||||
inquote = FALSE; /* This shouldn't really make a difference */
|
||||
} while (!feof (File.fp));
|
||||
}
|
||||
|
||||
extern parserDefinition* BetaParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "bet", NULL };
|
||||
parserDefinition* def = parserNew ("BETA");
|
||||
def->kinds = BetaKinds;
|
||||
def->kindCount = KIND_COUNT (BetaKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findBetaTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
+2838
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* $Id: cobol.c,v 1.6 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for COBOL language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installCobolRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language, "^[ \t]*[0-9]+[ \t]+([A-Z0-9][A-Z0-9-]*)[ \t]+(BLANK|OCCURS|IS|JUST|PIC|REDEFINES|RENAMES|SIGN|SYNC|USAGE|VALUE)",
|
||||
"\\1", "d,data,data items", "i");
|
||||
addTagRegex (language, "^[ \t]*[FSR]D[ \t]+([A-Z0-9][A-Z0-9-]*)\\.",
|
||||
"\\1", "f,file,file descriptions (FD, SD, RD)", "i");
|
||||
addTagRegex (language, "^[ \t]*[0-9]+[ \t]+([A-Z0-9][A-Z0-9-]*)\\.",
|
||||
"\\1", "g,group,group items", "i");
|
||||
addTagRegex (language, "^[ \t]*([A-Z0-9][A-Z0-9-]*)\\.",
|
||||
"\\1", "p,paragraph,paragraphs", "i");
|
||||
addTagRegex (language, "^[ \t]*PROGRAM-ID\\.[ \t]+([A-Z0-9][A-Z0-9-]*)\\.",
|
||||
"\\1", "P,program,program ids", "i");
|
||||
addTagRegex (language, "^[ \t]*([A-Z0-9][A-Z0-9-]*)[ \t]+SECTION\\.",
|
||||
"\\1", "s,section,sections", "i");
|
||||
}
|
||||
|
||||
extern parserDefinition* CobolParser ()
|
||||
{
|
||||
static const char *const extensions [] = {
|
||||
"cbl", "cob", "CBL", "COB", NULL };
|
||||
parserDefinition* def = parserNew ("Cobol");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installCobolRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,275 @@
|
||||
/* config.h. Generated by configure. */
|
||||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
/*
|
||||
* $Id: acconfig.h,v 1.4 2003/04/01 05:02:21 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains input source for generating config.h.in
|
||||
*/
|
||||
|
||||
/* Package name.
|
||||
*/
|
||||
/* #undef PACKAGE */
|
||||
|
||||
/* Package version.
|
||||
*/
|
||||
/* #undef VERSION */
|
||||
|
||||
/* Define to the appropriate type if <time.h> does not define this.
|
||||
*/
|
||||
/* #undef clock_t */
|
||||
|
||||
/* Define to long if <stdio.h> does not define this.
|
||||
*/
|
||||
/* #undef fpos_t */
|
||||
|
||||
/* Define to the appropriate size for tmpnam() if <stdio.h> does not define
|
||||
* this.
|
||||
*/
|
||||
/* #undef L_tmpnam */
|
||||
|
||||
/* Define this macro if the field "st_ino" exists in struct stat in
|
||||
* <sys/stat.h>.
|
||||
* */
|
||||
#define HAVE_STAT_ST_INO 1
|
||||
|
||||
/* Define remove to unlink if you have unlink(), but not remove().
|
||||
*/
|
||||
/* #undef remove */
|
||||
|
||||
/* Define this value used by fseek() appropriately if <stdio.h>
|
||||
* (or <unistd.h> on SunOS 4.1.x) does not define them.
|
||||
*/
|
||||
/* #undef SEEK_SET */
|
||||
|
||||
/* Define as the maximum integer on your system if not defined <limits.h>.
|
||||
*/
|
||||
/* #undef INT_MAX */
|
||||
|
||||
/* You can define this label to be a string containing the name of a
|
||||
* site-specific configuration file containing site-wide default options. The
|
||||
* files /etc/ctags.conf and /usr/local/etc/ctags.conf are already checked,
|
||||
* so only define one here if you need a file somewhere else.
|
||||
*/
|
||||
/* #undef CUSTOM_CONFIGURATION_FILE */
|
||||
|
||||
/* Define this label if you want macro tags (defined lables) to use patterns
|
||||
* in the EX command by default (original ctags behavior is to use line
|
||||
* numbers).
|
||||
*/
|
||||
/* #undef MACROS_USE_PATTERNS */
|
||||
|
||||
/* Define this as desired.
|
||||
* 1: Original ctags format
|
||||
* 2: Extended ctags format with extension flags in EX-style comment.
|
||||
*/
|
||||
#define DEFAULT_FILE_FORMAT 2
|
||||
|
||||
/* Define this label if your system supports starting scripts with a line of
|
||||
* the form "#! /bin/sh" to select the interpreter to use for the script.
|
||||
*/
|
||||
#define SYS_INTERPRETER 1
|
||||
|
||||
/* Define this label if your system uses case-insensitive file names
|
||||
*/
|
||||
/* #undef CASE_INSENSITIVE_FILENAMES */
|
||||
|
||||
/* Define this label to use the system sort utility (which is probably more
|
||||
* efficient) over the internal sorting algorithm.
|
||||
*/
|
||||
#ifndef INTERNAL_SORT
|
||||
# define EXTERNAL_SORT 1
|
||||
#endif
|
||||
|
||||
/* If you wish to change the directory in which temporary files are stored,
|
||||
* define this label to the directory desired.
|
||||
*/
|
||||
#define TMPDIR "/tmp"
|
||||
|
||||
/* Define this label if regcomp() is broken.
|
||||
*/
|
||||
/* #undef REGCOMP_BROKEN */
|
||||
|
||||
/* Define this label if you wish to check the regcomp() function at run time
|
||||
* for correct behavior. This function is currently broken on Cygwin.
|
||||
*/
|
||||
/* #undef CHECK_REGCOMP */
|
||||
|
||||
/* This corrects the problem of missing prototypes for certain functions
|
||||
* in some GNU installations (e.g. SunOS 4.1.x).
|
||||
*/
|
||||
/* #undef __USE_FIXED_PROTOTYPES__ */
|
||||
|
||||
/* Define this is you have a prototype for putenv() in <stdlib.h>, but
|
||||
* doesn't declare its argument as "const char *".
|
||||
*/
|
||||
/* #undef NON_CONST_PUTENV_PROTOTYPE */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing
|
||||
* a prototype for, or a type mismatch using, one of the following functions,
|
||||
* define the appropriate label and remake.
|
||||
*/
|
||||
/* #undef NEED_PROTO_REMOVE */
|
||||
/* #undef NEED_PROTO_UNLINK */
|
||||
/* #undef NEED_PROTO_MALLOC */
|
||||
/* #undef NEED_PROTO_GETENV */
|
||||
/* #undef NEED_PROTO_FGETPOS */
|
||||
/* #undef NEED_PROTO_STAT */
|
||||
/* #undef NEED_PROTO_LSTAT */
|
||||
/* #undef NEED_PROTO_TRUNCATE */
|
||||
/* #undef NEED_PROTO_FTRUNCATE */
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
- Lines below this are automatically generated by autoheader
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/* Define to 1 if you have the `chmod' function. */
|
||||
/* #undef HAVE_CHMOD */
|
||||
|
||||
/* Define to 1 if you have the `chsize' function. */
|
||||
/* #undef HAVE_CHSIZE */
|
||||
|
||||
/* Define to 1 if you have the `clock' function. */
|
||||
#define HAVE_CLOCK 1
|
||||
|
||||
/* Define to 1 if you have the <dirent.h> header file. */
|
||||
#define HAVE_DIRENT_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `fgetpos' function. */
|
||||
#define HAVE_FGETPOS 1
|
||||
|
||||
/* Define to 1 if you have the `findfirst' function. */
|
||||
/* #undef HAVE_FINDFIRST */
|
||||
|
||||
/* Define to 1 if you have the `fnmatch' function. */
|
||||
#define HAVE_FNMATCH 1
|
||||
|
||||
/* Define to 1 if you have the <fnmatch.h> header file. */
|
||||
#define HAVE_FNMATCH_H 1
|
||||
|
||||
/* Define to 1 if you have the `ftruncate' function. */
|
||||
/* #undef HAVE_FTRUNCATE */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the `mkstemp' function. */
|
||||
#define HAVE_MKSTEMP 1
|
||||
|
||||
/* Define to 1 if you have the `opendir' function. */
|
||||
#define HAVE_OPENDIR 1
|
||||
|
||||
/* Define to 1 if you have the `putenv' function. */
|
||||
/* #undef HAVE_PUTENV */
|
||||
|
||||
/* Define to 1 if you have the `regcomp' function. */
|
||||
#define HAVE_REGCOMP 1
|
||||
|
||||
/* Define to 1 if you have the `remove' function. */
|
||||
#define HAVE_REMOVE 1
|
||||
|
||||
/* Define to 1 if you have the `setenv' function. */
|
||||
#define HAVE_SETENV 1
|
||||
|
||||
/* Define to 1 if you have the <stat.h> header file. */
|
||||
/* #undef HAVE_STAT_H */
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the `strcasecmp' function. */
|
||||
#define HAVE_STRCASECMP 1
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* Define to 1 if you have the `stricmp' function. */
|
||||
/* #undef HAVE_STRICMP */
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strncasecmp' function. */
|
||||
#define HAVE_STRNCASECMP 1
|
||||
|
||||
/* Define to 1 if you have the `strnicmp' function. */
|
||||
/* #undef HAVE_STRNICMP */
|
||||
|
||||
/* Define to 1 if you have the `strstr' function. */
|
||||
#define HAVE_STRSTR 1
|
||||
|
||||
/* Define to 1 if you have the <sys/dir.h> header file. */
|
||||
/* #undef HAVE_SYS_DIR_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/times.h> header file. */
|
||||
#define HAVE_SYS_TIMES_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `tempnam' function. */
|
||||
/* #undef HAVE_TEMPNAM */
|
||||
|
||||
/* Define to 1 if you have the `times' function. */
|
||||
/* #undef HAVE_TIMES */
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#define HAVE_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the `truncate' function. */
|
||||
#define HAVE_TRUNCATE 1
|
||||
|
||||
/* Define to 1 if you have the <types.h> header file. */
|
||||
/* #undef HAVE_TYPES_H */
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `_findfirst' function. */
|
||||
/* #undef HAVE__FINDFIRST */
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME ""
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING ""
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION ""
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `long' if <sys/types.h> does not define. */
|
||||
/* #undef off_t */
|
||||
|
||||
/* Define to `unsigned' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
@@ -0,0 +1,274 @@
|
||||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
/*
|
||||
* $Id: acconfig.h,v 1.4 2003/04/01 05:02:21 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains input source for generating config.h.in
|
||||
*/
|
||||
|
||||
/* Package name.
|
||||
*/
|
||||
#undef PACKAGE
|
||||
|
||||
/* Package version.
|
||||
*/
|
||||
#undef VERSION
|
||||
|
||||
/* Define to the appropriate type if <time.h> does not define this.
|
||||
*/
|
||||
#undef clock_t
|
||||
|
||||
/* Define to long if <stdio.h> does not define this.
|
||||
*/
|
||||
#undef fpos_t
|
||||
|
||||
/* Define to the appropriate size for tmpnam() if <stdio.h> does not define
|
||||
* this.
|
||||
*/
|
||||
#undef L_tmpnam
|
||||
|
||||
/* Define this macro if the field "st_ino" exists in struct stat in
|
||||
* <sys/stat.h>.
|
||||
* */
|
||||
#undef HAVE_STAT_ST_INO
|
||||
|
||||
/* Define remove to unlink if you have unlink(), but not remove().
|
||||
*/
|
||||
#undef remove
|
||||
|
||||
/* Define this value used by fseek() appropriately if <stdio.h>
|
||||
* (or <unistd.h> on SunOS 4.1.x) does not define them.
|
||||
*/
|
||||
#undef SEEK_SET
|
||||
|
||||
/* Define as the maximum integer on your system if not defined <limits.h>.
|
||||
*/
|
||||
#undef INT_MAX
|
||||
|
||||
/* You can define this label to be a string containing the name of a
|
||||
* site-specific configuration file containing site-wide default options. The
|
||||
* files /etc/ctags.conf and /usr/local/etc/ctags.conf are already checked,
|
||||
* so only define one here if you need a file somewhere else.
|
||||
*/
|
||||
#undef CUSTOM_CONFIGURATION_FILE
|
||||
|
||||
/* Define this label if you want macro tags (defined lables) to use patterns
|
||||
* in the EX command by default (original ctags behavior is to use line
|
||||
* numbers).
|
||||
*/
|
||||
#undef MACROS_USE_PATTERNS
|
||||
|
||||
/* Define this as desired.
|
||||
* 1: Original ctags format
|
||||
* 2: Extended ctags format with extension flags in EX-style comment.
|
||||
*/
|
||||
#define DEFAULT_FILE_FORMAT 2
|
||||
|
||||
/* Define this label if your system supports starting scripts with a line of
|
||||
* the form "#! /bin/sh" to select the interpreter to use for the script.
|
||||
*/
|
||||
#undef SYS_INTERPRETER
|
||||
|
||||
/* Define this label if your system uses case-insensitive file names
|
||||
*/
|
||||
#undef CASE_INSENSITIVE_FILENAMES
|
||||
|
||||
/* Define this label to use the system sort utility (which is probably more
|
||||
* efficient) over the internal sorting algorithm.
|
||||
*/
|
||||
#ifndef INTERNAL_SORT
|
||||
# undef EXTERNAL_SORT
|
||||
#endif
|
||||
|
||||
/* If you wish to change the directory in which temporary files are stored,
|
||||
* define this label to the directory desired.
|
||||
*/
|
||||
#undef TMPDIR
|
||||
|
||||
/* Define this label if regcomp() is broken.
|
||||
*/
|
||||
#undef REGCOMP_BROKEN
|
||||
|
||||
/* Define this label if you wish to check the regcomp() function at run time
|
||||
* for correct behavior. This function is currently broken on Cygwin.
|
||||
*/
|
||||
#undef CHECK_REGCOMP
|
||||
|
||||
/* This corrects the problem of missing prototypes for certain functions
|
||||
* in some GNU installations (e.g. SunOS 4.1.x).
|
||||
*/
|
||||
#undef __USE_FIXED_PROTOTYPES__
|
||||
|
||||
/* Define this is you have a prototype for putenv() in <stdlib.h>, but
|
||||
* doesn't declare its argument as "const char *".
|
||||
*/
|
||||
#undef NON_CONST_PUTENV_PROTOTYPE
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing
|
||||
* a prototype for, or a type mismatch using, one of the following functions,
|
||||
* define the appropriate label and remake.
|
||||
*/
|
||||
#undef NEED_PROTO_REMOVE
|
||||
#undef NEED_PROTO_UNLINK
|
||||
#undef NEED_PROTO_MALLOC
|
||||
#undef NEED_PROTO_GETENV
|
||||
#undef NEED_PROTO_FGETPOS
|
||||
#undef NEED_PROTO_STAT
|
||||
#undef NEED_PROTO_LSTAT
|
||||
#undef NEED_PROTO_TRUNCATE
|
||||
#undef NEED_PROTO_FTRUNCATE
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
- Lines below this are automatically generated by autoheader
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/* Define to 1 if you have the `chmod' function. */
|
||||
#undef HAVE_CHMOD
|
||||
|
||||
/* Define to 1 if you have the `chsize' function. */
|
||||
#undef HAVE_CHSIZE
|
||||
|
||||
/* Define to 1 if you have the `clock' function. */
|
||||
#undef HAVE_CLOCK
|
||||
|
||||
/* Define to 1 if you have the <dirent.h> header file. */
|
||||
#undef HAVE_DIRENT_H
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#undef HAVE_FCNTL_H
|
||||
|
||||
/* Define to 1 if you have the `fgetpos' function. */
|
||||
#undef HAVE_FGETPOS
|
||||
|
||||
/* Define to 1 if you have the `findfirst' function. */
|
||||
#undef HAVE_FINDFIRST
|
||||
|
||||
/* Define to 1 if you have the `fnmatch' function. */
|
||||
#undef HAVE_FNMATCH
|
||||
|
||||
/* Define to 1 if you have the <fnmatch.h> header file. */
|
||||
#undef HAVE_FNMATCH_H
|
||||
|
||||
/* Define to 1 if you have the `ftruncate' function. */
|
||||
#undef HAVE_FTRUNCATE
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `mkstemp' function. */
|
||||
#undef HAVE_MKSTEMP
|
||||
|
||||
/* Define to 1 if you have the `opendir' function. */
|
||||
#undef HAVE_OPENDIR
|
||||
|
||||
/* Define to 1 if you have the `putenv' function. */
|
||||
#undef HAVE_PUTENV
|
||||
|
||||
/* Define to 1 if you have the `regcomp' function. */
|
||||
#undef HAVE_REGCOMP
|
||||
|
||||
/* Define to 1 if you have the `remove' function. */
|
||||
#undef HAVE_REMOVE
|
||||
|
||||
/* Define to 1 if you have the `setenv' function. */
|
||||
#undef HAVE_SETENV
|
||||
|
||||
/* Define to 1 if you have the <stat.h> header file. */
|
||||
#undef HAVE_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `strcasecmp' function. */
|
||||
#undef HAVE_STRCASECMP
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#undef HAVE_STRERROR
|
||||
|
||||
/* Define to 1 if you have the `stricmp' function. */
|
||||
#undef HAVE_STRICMP
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the `strncasecmp' function. */
|
||||
#undef HAVE_STRNCASECMP
|
||||
|
||||
/* Define to 1 if you have the `strnicmp' function. */
|
||||
#undef HAVE_STRNICMP
|
||||
|
||||
/* Define to 1 if you have the `strstr' function. */
|
||||
#undef HAVE_STRSTR
|
||||
|
||||
/* Define to 1 if you have the <sys/dir.h> header file. */
|
||||
#undef HAVE_SYS_DIR_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/times.h> header file. */
|
||||
#undef HAVE_SYS_TIMES_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the `tempnam' function. */
|
||||
#undef HAVE_TEMPNAM
|
||||
|
||||
/* Define to 1 if you have the `times' function. */
|
||||
#undef HAVE_TIMES
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#undef HAVE_TIME_H
|
||||
|
||||
/* Define to 1 if you have the `truncate' function. */
|
||||
#undef HAVE_TRUNCATE
|
||||
|
||||
/* Define to 1 if you have the <types.h> header file. */
|
||||
#undef HAVE_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to 1 if you have the `_findfirst' function. */
|
||||
#undef HAVE__FINDFIRST
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
/* Define to `long' if <sys/types.h> does not define. */
|
||||
#undef off_t
|
||||
|
||||
/* Define to `unsigned' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
+7413
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,423 @@
|
||||
dnl $Id: configure.in,v 1.13 2006/05/30 04:52:27 darren Exp $
|
||||
|
||||
dnl Copyright (c) 1996-2002, Darren Hiebert
|
||||
dnl
|
||||
dnl This source code is released for free distribution under the terms
|
||||
dnl of the GNU General Public License.
|
||||
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(1.12)
|
||||
AC_INIT(ctags.h)
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
||||
dnl Report system info
|
||||
dnl ------------------
|
||||
echo 'Exuberant Ctags, version 5.6'
|
||||
uname -mrsv 2>/dev/null
|
||||
|
||||
dnl Define convenience macros
|
||||
dnl -------------------------
|
||||
dnl CHECK_HEADER_DEFINE(LABEL, HEADER [,ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ] ])
|
||||
AC_DEFUN(CHECK_HEADER_DEFINE,
|
||||
[
|
||||
AC_MSG_CHECKING("if $1 is defined in $2")
|
||||
AC_EGREP_CPP(yes,
|
||||
[#include <$2>
|
||||
#ifdef $1
|
||||
yes
|
||||
#endif
|
||||
], [
|
||||
AC_MSG_RESULT(yes)
|
||||
$3
|
||||
], [
|
||||
AC_MSG_RESULT(no)
|
||||
$4
|
||||
]) ])
|
||||
|
||||
dnl Checks for configuration options
|
||||
dnl --------------------------------
|
||||
|
||||
AC_ARG_WITH(posix-regex,
|
||||
[ --with-posix-regex use Posix regex interface, if available])
|
||||
|
||||
AC_ARG_WITH(readlib,
|
||||
[ --with-readlib include readtags library object during install])
|
||||
|
||||
dnl AC_ARG_WITH(perl-regex,
|
||||
dnl [ --with-perl-regex use Perl pcre interface, if available])
|
||||
|
||||
AC_ARG_ENABLE(etags,
|
||||
[ --enable-etags enable the installation of links for etags])
|
||||
|
||||
AC_ARG_ENABLE(extended-format,
|
||||
[ --disable-extended-format
|
||||
disable extension flags; use original ctags file
|
||||
format only],
|
||||
AC_DEFINE(DEFAULT_FILE_FORMAT, 1), AC_DEFINE(DEFAULT_FILE_FORMAT, 2))
|
||||
|
||||
AC_ARG_ENABLE(external-sort,
|
||||
[ --disable-external-sort use internal sort algorithm instead of sort program])
|
||||
|
||||
AC_ARG_ENABLE(custom-config,
|
||||
[ --enable-custom-config=FILE
|
||||
enable custom config file for site-wide defaults])
|
||||
|
||||
AC_ARG_ENABLE(macro-patterns,
|
||||
[ --enable-macro-patterns use patterns as default method to locate macros
|
||||
instead of line numbers])
|
||||
|
||||
AC_ARG_ENABLE(maintainer-mode,
|
||||
[ --enable-maintainer-mode
|
||||
use maintainer makefile])
|
||||
|
||||
AC_ARG_ENABLE(shell-globbing,
|
||||
[ --enable-shell-globbing=DIR
|
||||
does shell expand wildcards (yes|no)? [yes]])
|
||||
|
||||
AC_ARG_ENABLE(tmpdir,
|
||||
[ --enable-tmpdir=DIR default directory for temporary files [ARG=/tmp]],
|
||||
tmpdir_specified=yes)
|
||||
|
||||
|
||||
dnl Process configuration options
|
||||
dnl -----------------------------
|
||||
|
||||
if test "$enable_maintainer_mode" = yes ; then
|
||||
AC_MSG_RESULT(enabling maintainer mode)
|
||||
fi
|
||||
|
||||
install_targets="install-ctags"
|
||||
AC_MSG_CHECKING(whether to install link to etags)
|
||||
if test yes = "$enable_etags"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
install_targets="$install_targets install-etags"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AC_MSG_CHECKING(whether to install readtags object file)
|
||||
if test yes = "$with_readlib"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
install_targets="$install_targets install-lib"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AC_SUBST(install_targets)
|
||||
|
||||
if test "$enable_custom_config" = no -o "$enable_custom_config" = yes ; then
|
||||
AC_MSG_RESULT(no name supplied for custom configuration file)
|
||||
elif test -n "$enable_custom_config" ; then
|
||||
AC_DEFINE_UNQUOTED(CUSTOM_CONFIGURATION_FILE, "$enable_custom_config")
|
||||
AC_MSG_RESULT($enable_custom_config will be used as custom configuration file)
|
||||
fi
|
||||
|
||||
if test "$enable_macro_patterns" = yes ; then
|
||||
AC_DEFINE(MACROS_USE_PATTERNS)
|
||||
AC_MSG_RESULT(tag file will use patterns for macros by default)
|
||||
fi
|
||||
|
||||
dnl Checks for programs
|
||||
dnl -------------------
|
||||
|
||||
AC_PROG_CC
|
||||
|
||||
case `uname` in
|
||||
HP-UX)
|
||||
AC_MSG_CHECKING("HP-UX native compiler")
|
||||
if test "$CC" = "cc"; then
|
||||
AC_MSG_RESULT(yes; adding compiler options for ANSI support)
|
||||
CFLAGS="$CFLAGS -Aa -D_HPUX_SOURCE"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
;;
|
||||
SunOS)
|
||||
if uname -r | grep '5\.' >/dev/null 2>&1; then
|
||||
AC_MSG_CHECKING("Solaris native compiler")
|
||||
if test "$CC" = "cc" -a "`which cc`" = "/usr/ucb/cc"; then
|
||||
AC_MSG_RESULT(yes; adding compiler option for ANSI support)
|
||||
CC="$CC -Xa"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_PROG_LN_S
|
||||
AC_CHECK_PROG(STRIP, strip, strip, :)
|
||||
|
||||
|
||||
dnl Checks for operating environment
|
||||
dnl --------------------------------
|
||||
|
||||
dnl Check for temporary directory
|
||||
AC_MSG_CHECKING(directory to use for temporary files)
|
||||
if test -n "$enable_tmpdir"; then
|
||||
tmpdir="$enable_tmpdir"
|
||||
elif test -n "$TMPDIR"; then
|
||||
tmpdir="$TMPDIR"
|
||||
elif test -n "$TMPDIR"; then
|
||||
tmpdir="$TMPDIR"
|
||||
elif test -n "$TMP"; then
|
||||
tmpdir="$TMP"
|
||||
elif test -n "$TEMP"; then
|
||||
tmpdir="$TEMP"
|
||||
elif test -d "c:/"; then
|
||||
tmpdir="c:/"
|
||||
else
|
||||
tmpdir="/tmp"
|
||||
fi
|
||||
if test -d $tmpdir ; then
|
||||
AC_MSG_RESULT($tmpdir)
|
||||
AC_DEFINE_UNQUOTED(TMPDIR, "$tmpdir")
|
||||
else
|
||||
AC_MSG_ERROR($tmpdir does not exist)
|
||||
fi
|
||||
|
||||
dnl Check whether system supports #! scripts
|
||||
AC_SYS_INTERPRETER
|
||||
if test yes = "$interpval"; then
|
||||
AC_DEFINE(SYS_INTERPRETER)
|
||||
fi
|
||||
|
||||
dnl Test for case-insensitive filenames
|
||||
AC_MSG_CHECKING("for case-insensitive filenames")
|
||||
touch conftest.cif
|
||||
if test -f CONFTEST.CIF; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(CASE_INSENSITIVE_FILENAMES)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
rm -f conftest.cif
|
||||
|
||||
AC_MSG_CHECKING(selected sort method)
|
||||
if test no = "$enable_external_sort"; then
|
||||
AC_MSG_RESULT(simple internal algorithm)
|
||||
else
|
||||
AC_MSG_RESULT(external sort utility)
|
||||
enable_external_sort=no
|
||||
AC_CHECK_PROG(sort_found, sort, yes, no)
|
||||
if test "$sort_found" = yes ; then
|
||||
AC_MSG_CHECKING(if sort accepts our command line)
|
||||
touch ${tmpdir}/sort.test
|
||||
sort -u -f -o ${tmpdir}/sort.test ${tmpdir}/sort.test 1>/dev/null 2>&1
|
||||
if test $? -ne 0 ; then
|
||||
AC_MSG_RESULT(no)
|
||||
else
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(EXTERNAL_SORT)
|
||||
enable_external_sort=yes
|
||||
fi
|
||||
rm -f ${tmpdir}/sort.test
|
||||
fi
|
||||
fi
|
||||
if test "$enable_external_sort" != yes ; then
|
||||
AC_MSG_RESULT(using internal sort algorithm as fallback)
|
||||
fi
|
||||
|
||||
|
||||
dnl Checks for header files
|
||||
dnl -----------------------
|
||||
|
||||
AC_CHECK_HEADERS(dirent.h fcntl.h fnmatch.h stat.h stdlib.h string.h)
|
||||
AC_CHECK_HEADERS(time.h types.h unistd.h)
|
||||
AC_CHECK_HEADERS(sys/dir.h sys/stat.h sys/times.h sys/types.h)
|
||||
|
||||
|
||||
dnl Checks for header file macros
|
||||
dnl -----------------------------
|
||||
|
||||
CHECK_HEADER_DEFINE(L_tmpnam, stdio.h,, AC_DEFINE(L_tmpnam, 20))
|
||||
|
||||
CHECK_HEADER_DEFINE(INT_MAX, limits.h,,
|
||||
CHECK_HEADER_DEFINE(MAXINT, limits.h,
|
||||
AC_DEFINE(INT_MAX, MAXINT), AC_DEFINE(INT_MAX, 32767)))
|
||||
|
||||
|
||||
dnl Checks for typedefs
|
||||
dnl -------------------
|
||||
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_OFF_T
|
||||
|
||||
AC_MSG_CHECKING("for fpos_t")
|
||||
AC_EGREP_HEADER(fpos_t, stdio.h, AC_MSG_RESULT(yes),
|
||||
[
|
||||
AC_MSG_RESULT(no)
|
||||
AC_DEFINE(fpos_t, long)
|
||||
])
|
||||
|
||||
AC_MSG_CHECKING("for clock_t")
|
||||
AC_EGREP_HEADER(clock_t, time.h, AC_MSG_RESULT(yes),
|
||||
[
|
||||
AC_MSG_RESULT(no)
|
||||
AC_DEFINE(clock_t, long)
|
||||
])
|
||||
|
||||
|
||||
dnl Checks for compiler characteristics
|
||||
dnl -----------------------------------
|
||||
|
||||
dnl AC_CYGWIN
|
||||
dnl AC_MINGW32
|
||||
AC_C_CONST
|
||||
AC_OBJEXT
|
||||
AC_EXEEXT
|
||||
|
||||
AC_MSG_CHECKING(if struct stat contains st_ino)
|
||||
AC_TRY_COMPILE([#include <sys/stat.h>], [
|
||||
struct stat st;
|
||||
stat(".", &st);
|
||||
if (st.st_ino > 0)
|
||||
exit(0);
|
||||
], have_st_ino=yes, have_st_ino=no)
|
||||
AC_MSG_RESULT($have_st_ino)
|
||||
if test yes = "$have_st_ino"; then
|
||||
AC_DEFINE(HAVE_STAT_ST_INO)
|
||||
fi
|
||||
|
||||
|
||||
dnl Checks for library functions
|
||||
dnl ----------------------------
|
||||
|
||||
AC_CHECK_FUNCS(fnmatch)
|
||||
AC_CHECK_FUNCS(strstr)
|
||||
AC_CHECK_FUNCS(strcasecmp stricmp, break)
|
||||
AC_CHECK_FUNCS(strncasecmp strnicmp, break)
|
||||
AC_CHECK_FUNCS(fgetpos, have_fgetpos=yes)
|
||||
|
||||
dnl SEEK_SET should be in stdio.h, but may be in unistd.h on SunOS 4.1.x
|
||||
if test "$have_fgetpos" != yes ; then
|
||||
CHECK_HEADER_DEFINE(SEEK_SET, stdio.h,,
|
||||
CHECK_HEADER_DEFINE(SEEK_SET, unistd.h,,
|
||||
AC_DEFINE(SEEK_SET, 0)))
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(mkstemp, have_mkstemp=yes)
|
||||
if test "$have_mkstemp" != yes ; then
|
||||
AC_CHECK_FUNCS(tempnam, have_tempnam=yes)
|
||||
fi
|
||||
if test "$have_mkstemp" != yes -a "$have_tempnam" != yes; then
|
||||
AC_CHECK_FUNCS(chmod)
|
||||
if test "$tmpdir_specified" = yes ; then
|
||||
AC_MSG_RESULT(use of tmpnam overrides temporary directory selection)
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(opendir findfirst _findfirst, break)
|
||||
AC_CHECK_FUNCS(strerror)
|
||||
AC_CHECK_FUNCS(clock times, break)
|
||||
AC_CHECK_FUNCS(remove, have_remove=yes,
|
||||
CHECK_HEADER_DEFINE(remove, unistd.h,, AC_DEFINE(remove, unlink)))
|
||||
|
||||
AC_CHECK_FUNCS(truncate, have_truncate=yes)
|
||||
dnl === Cannot nest AC_CHECK_FUNCS() calls
|
||||
if test "$have_truncate" != yes ; then
|
||||
AC_CHECK_FUNCS(ftruncate, have_ftruncate=yes)
|
||||
if test "$have_ftruncate" != yes ; then
|
||||
AC_CHECK_FUNCS(chsize)
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(setenv, have_setenv=yes)
|
||||
dnl === Cannot nest AC_CHECK_FUNCS() calls
|
||||
if test "$have_setenv" != yes ; then
|
||||
AC_CHECK_FUNCS(putenv, have_putenv=yes)
|
||||
if test "$have_putenv" = yes ; then
|
||||
AC_EGREP_HEADER(putenv, stdlib.h, have_putenv_prototype=yes)
|
||||
if test "$have_putenv_prototype" = yes ; then
|
||||
AC_MSG_CHECKING("putenv prototype")
|
||||
AC_EGREP_HEADER([[^A-Za-zo-9_]putenv[ ]*\(.*const.*\)[ ]*;],
|
||||
stdlib.h, AC_MSG_RESULT(correct),
|
||||
[
|
||||
AC_MSG_RESULT(no const)
|
||||
AC_DEFINE(NON_CONST_PUTENV_PROTOTYPE)
|
||||
])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl if test yes = "$CYGWIN"; then with_posix_regex=no; fi
|
||||
if test no != "$with_posix_regex"; then
|
||||
AC_CHECK_FUNCS(regcomp)
|
||||
AC_MSG_CHECKING(if regcomp works)
|
||||
AC_TRY_RUN([
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
main() {
|
||||
regex_t patbuf;
|
||||
exit (regcomp (&patbuf, "/hello/", 0) != 0);
|
||||
}],regcomp_works=yes,regcomp_works=no,AC_DEFINE(CHECK_REGCOMP))
|
||||
AC_MSG_RESULT($regcomp_works)
|
||||
if test yes != "$regcomp_works"; then
|
||||
AC_DEFINE(REGCOMP_BROKEN)
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl if test yes = "$with_perl_regex"; then
|
||||
dnl AC_MSG_CHECKING(for Perl regex library)
|
||||
dnl pcre_candidates="$with_perl_regex $HOME/local/lib* /usr*/local/lib* /usr/lib*"
|
||||
dnl for lib in $pcre_candidates; do
|
||||
dnl if test -f $lib/libpcreposix.so; then
|
||||
dnl pcre_lib="-L$lib -lpcreposix"
|
||||
dnl break
|
||||
dnl elif test -f $lib/libpcreposix.a; then
|
||||
dnl pcre_lib="$lib/libpcreposix.a"
|
||||
dnl break
|
||||
dnl fi
|
||||
dnl done
|
||||
dnl if test -z "$pcre_lib"; then
|
||||
dnl AC_MSG_RESULT(not found)
|
||||
dnl else
|
||||
dnl AC_MSG_RESULT($lib)
|
||||
dnl AC_DEFINE(HAVE_REGCOMP)
|
||||
dnl LDFLAGS="$LDFLAGS $pcre_lib"
|
||||
dnl have_regex=yes
|
||||
dnl fi
|
||||
dnl fi
|
||||
|
||||
|
||||
dnl Checks for missing prototypes
|
||||
dnl -----------------------------
|
||||
AC_CHECKING("for missing prototypes")
|
||||
|
||||
AC_DEFUN(CHECK_PROTO, [AC_EGREP_HEADER([[^A-Za-z0-9_]$1([ ]+[A-Za-z0-9_]*)?\(], $2,, AC_DEFINE(patsubst([NEED_PROTO_NAME], [NAME], translit($1, [a-z], [A-Z]))) AC_MSG_RESULT(adding prototype for $1))])
|
||||
|
||||
if test "$have_remove" = yes ; then
|
||||
CHECK_PROTO(remove, stdio.h)
|
||||
else
|
||||
CHECK_PROTO(unlink, unistd.h)
|
||||
fi
|
||||
CHECK_PROTO(malloc, stdlib.h)
|
||||
CHECK_PROTO(getenv, stdlib.h)
|
||||
CHECK_PROTO(stat, sys/stat.h)
|
||||
CHECK_PROTO(lstat, sys/stat.h)
|
||||
if test "$have_fgetpos" = yes ; then
|
||||
CHECK_PROTO(fgetpos, stdio.h)
|
||||
fi
|
||||
if test "$have_truncate" = yes ; then
|
||||
CHECK_PROTO(truncate, unistd.h)
|
||||
fi
|
||||
if test "$have_ftruncate" = yes ; then
|
||||
CHECK_PROTO(ftruncate, unistd.h)
|
||||
fi
|
||||
|
||||
|
||||
dnl Output files
|
||||
dnl ------------
|
||||
|
||||
|
||||
rm -f Makefile
|
||||
if test "$enable_maintainer_mode" = yes ; then
|
||||
AC_MSG_RESULT(creating maintainer Makefile)
|
||||
ln -s maintainer.mak Makefile
|
||||
makefile_out=
|
||||
else
|
||||
makefile_out=Makefile
|
||||
fi
|
||||
AC_OUTPUT($makefile_out)
|
||||
|
||||
dnl vim:ts=4:sw=4:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* $Id: ctags.h,v 1.4 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Program definitions
|
||||
*/
|
||||
#ifndef _CTAGS_H
|
||||
#define _CTAGS_H
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#ifndef PROGRAM_VERSION
|
||||
# define PROGRAM_VERSION "5.6"
|
||||
#endif
|
||||
#define PROGRAM_NAME "Exuberant Ctags"
|
||||
#define PROGRAM_URL "http://ctags.sourceforge.net"
|
||||
#define AUTHOR_NAME "Darren Hiebert"
|
||||
#define AUTHOR_EMAIL "[email protected]"
|
||||
|
||||
#endif /* _CTAGS_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
Begin4
|
||||
Title: Exuberant Ctags
|
||||
Version: 5.6
|
||||
Entered-date: 2006-05-29
|
||||
Description: An improved ctags with multilanguage support.
|
||||
Keywords: tag tags ctags etags ftags ptags Exuberant Assembler ASP AWK
|
||||
Bourne sh BETA C C++ C# COBOL Eiffel Fortran Java JavaScript
|
||||
Korn ksh Lisp Lua Makefile Pascal Perl PHP PL/SQL Python REXX
|
||||
Ruby Scheme SLang S-Lang SML Tcl Vera Verilog Vim YACC zsh
|
||||
source index
|
||||
Author: [email protected] (Darren Hiebert)
|
||||
Maintained-by: [email protected] (Darren Hiebert)
|
||||
Primary-site: http://prdownloads.sourceforge.net/ctags
|
||||
260K ctags-5.6.tar.gz
|
||||
Original-site: http://ctags.sourceforge.net
|
||||
Platforms: UNIX, MSDOS, Windows 95/98/NT/2000/XP, OS/2, QNX, Amiga, QDOS,
|
||||
RISC OS, VMS, Macintosh, MacOS X, Cray
|
||||
Copying-policy: Gnu General Public License (GPL)
|
||||
End
|
||||
@@ -0,0 +1,11 @@
|
||||
resource app_version
|
||||
{
|
||||
major = 5,
|
||||
middle = 6,
|
||||
minor = 0,
|
||||
variety = B_APPV_FINAL,
|
||||
internal = 0,
|
||||
short_info = "ctags",
|
||||
long_info = "ctags from http://ctags.sourceforge.net/"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
Summary: Exuberant Ctags - a multi-language source code indexing tool
|
||||
Name: ctags
|
||||
Version: 5.6
|
||||
Release: 1
|
||||
License: GPL
|
||||
Group: Development/Tools
|
||||
Source: http://prdownloads.sourceforge.net/ctags/ctags-%{version}.tar.gz
|
||||
URL: http://ctags.sourceforge.net
|
||||
Buildroot: %{_tmppath}/%{name}-%{version}-root
|
||||
|
||||
%description
|
||||
Exuberant Ctags generates an index (or tag) file of language objects
|
||||
found in source files for many popular programming languages. This index
|
||||
makes it easy for text editors and other tools to locate the indexed
|
||||
items. Exuberant Ctags improves on traditional ctags because of its
|
||||
multilanguage support, its ability for the user to define new languages
|
||||
searched by regular expressions, and its ability to generate emacs-style
|
||||
TAGS files.
|
||||
|
||||
Install ctags if you are going to use your system for programming.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
%configure
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
%makeinstall
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc COPYING EXTENDING.html FAQ NEWS README ctags.html
|
||||
%{_bindir}/ctags
|
||||
%{_mandir}/man1/ctags*
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* $Id: debug.c,v 1.5 2006/05/30 04:37:11 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains debugging functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
extern void lineBreak (void) {} /* provides a line-specified break point */
|
||||
|
||||
extern void debugPrintf (
|
||||
const enum eDebugLevels level, const char *const format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, format);
|
||||
if (debug (level))
|
||||
vprintf (format, ap);
|
||||
fflush (stdout);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
extern void debugPutc (const int level, const int c)
|
||||
{
|
||||
if (debug (level) && c != EOF)
|
||||
{
|
||||
if (c == STRING_SYMBOL) printf ("\"string\"");
|
||||
else if (c == CHAR_SYMBOL) printf ("'c'");
|
||||
else putchar (c);
|
||||
|
||||
fflush (stdout);
|
||||
}
|
||||
}
|
||||
|
||||
extern void debugParseNest (const boolean increase, const unsigned int level)
|
||||
{
|
||||
debugPrintf (DEBUG_PARSE, "<*%snesting:%d*>", increase ? "++" : "--", level);
|
||||
}
|
||||
|
||||
extern void debugCppNest (const boolean begin, const unsigned int level)
|
||||
{
|
||||
debugPrintf (DEBUG_CPP, "<*cpp:%s level %d*>", begin ? "begin":"end", level);
|
||||
}
|
||||
|
||||
extern void debugCppIgnore (const boolean ignore)
|
||||
{
|
||||
debugPrintf (DEBUG_CPP, "<*cpp:%s ignore*>", ignore ? "begin":"end");
|
||||
}
|
||||
|
||||
extern void clearString (char *const string, const int length)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < length ; ++i)
|
||||
string [i] = '\0';
|
||||
}
|
||||
|
||||
extern void debugEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
const char *const scope = tag->isFileScope ? "{fs}" : "";
|
||||
|
||||
if (debug (DEBUG_PARSE))
|
||||
{
|
||||
printf ("<#%s%s:%s", scope, tag->kindName, tag->name);
|
||||
|
||||
if (tag->extensionFields.scope [0] != NULL &&
|
||||
tag->extensionFields.scope [1] != NULL)
|
||||
printf (" [%s:%s]", tag->extensionFields.scope [0],
|
||||
tag->extensionFields.scope [1]);
|
||||
|
||||
if (Option.extensionFields.inheritance &&
|
||||
tag->extensionFields.inheritance != NULL)
|
||||
printf (" [inherits:%s]", tag->extensionFields.inheritance);
|
||||
|
||||
if (Option.extensionFields.fileScope &&
|
||||
tag->isFileScope && ! isHeaderFile ())
|
||||
printf (" [file:]");
|
||||
|
||||
if (Option.extensionFields.access &&
|
||||
tag->extensionFields.access != NULL)
|
||||
printf (" [access:%s]", tag->extensionFields.access);
|
||||
|
||||
if (Option.extensionFields.implementation &&
|
||||
tag->extensionFields.implementation != NULL)
|
||||
printf (" [imp:%s]", tag->extensionFields.implementation);
|
||||
|
||||
if (Option.extensionFields.typeRef &&
|
||||
tag->extensionFields.typeRef [0] != NULL &&
|
||||
tag->extensionFields.typeRef [1] != NULL)
|
||||
printf (" [%s:%s]", tag->extensionFields.typeRef [0],
|
||||
tag->extensionFields.typeRef [1]);
|
||||
|
||||
printf ("#>");
|
||||
fflush (stdout);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* $Id: debug.h,v 1.3 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to debug.c
|
||||
*/
|
||||
#ifndef _DEBUG_H
|
||||
#define _DEBUG_H
|
||||
|
||||
/*
|
||||
* Include files
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#ifdef DEBUG
|
||||
# include <assert.h>
|
||||
#endif
|
||||
#include "entry.h"
|
||||
|
||||
/*
|
||||
* Macros
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
# define debug(level) ((Option.debugLevel & (long)(level)) != 0)
|
||||
# define DebugStatement(x) x
|
||||
# define PrintStatus(x) if (debug(DEBUG_STATUS)) printf x;
|
||||
# define Assert(c) assert(c)
|
||||
#else
|
||||
# define DebugStatement(x)
|
||||
# define PrintStatus(x)
|
||||
# define Assert(c)
|
||||
# ifndef NDEBUG
|
||||
# define NDEBUG
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Data declarations
|
||||
*/
|
||||
|
||||
/* Defines the debugging levels.
|
||||
*/
|
||||
enum eDebugLevels {
|
||||
DEBUG_READ = 0x01, /* echo raw (filtered) characters */
|
||||
DEBUG_PARSE = 0x02, /* echo parsing results */
|
||||
DEBUG_STATUS = 0x04, /* echo file status information */
|
||||
DEBUG_OPTION = 0x08, /* echo option parsing */
|
||||
DEBUG_CPP = 0x10, /* echo characters out of pre-processor */
|
||||
DEBUG_RAW = 0x20 /* echo raw (filtered) characters */
|
||||
};
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
extern void lineBreak (void);
|
||||
extern void debugPrintf (const enum eDebugLevels level, const char *const format, ...) __printf__ (2, 3);
|
||||
extern void debugPutc (const int level, const int c);
|
||||
extern void debugParseNest (const boolean increase, const unsigned int level);
|
||||
extern void debugCppNest (const boolean begin, const unsigned int level);
|
||||
extern void debugCppIgnore (const boolean ignore);
|
||||
extern void clearString (char *const string, const int length);
|
||||
extern void debugEntry (const tagEntryInfo *const tag);
|
||||
|
||||
#endif /* _DEBUG_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,68 @@
|
||||
# $Id: descrip.mms,v 1.1 2001/11/02 04:53:43 darren Exp $
|
||||
#
|
||||
# Makefile for building CTAGS under OpenVMS
|
||||
#
|
||||
# Maintained by by Zoltan Arpadffy <[email protected]>
|
||||
#
|
||||
# Edit the lines in the Configuration section below to select.
|
||||
|
||||
######################################################################
|
||||
# Configuration section.
|
||||
######################################################################
|
||||
# Compiler selection.
|
||||
# Comment out if you use the VAXC compiler
|
||||
######################################################################
|
||||
DECC = YES
|
||||
|
||||
######################################################################
|
||||
# Uncomment if want a debug version. Resulting executable is DCTAGS.EXE
|
||||
######################################################################
|
||||
# DEBUG = YES
|
||||
|
||||
######################################################################
|
||||
# End of configuration section.
|
||||
#
|
||||
# Please, do not change anything below without programming experience.
|
||||
######################################################################
|
||||
|
||||
CC = cc
|
||||
|
||||
.IFDEF DECC
|
||||
CC_DEF = $(CC)/decc/prefix=all
|
||||
.ELSE
|
||||
CC_DEF = $(CC)
|
||||
.ENDIF
|
||||
|
||||
LD_DEF = link
|
||||
|
||||
.IFDEF DEBUG
|
||||
TARGET = dctags.exe
|
||||
CFLAGS = /debug/noopt/list/cross_reference/include=[]
|
||||
LDFLAGS = /debug
|
||||
.ELSE
|
||||
TARGET = ctags.exe
|
||||
CFLAGS = /include=[]
|
||||
LDFLAGS =
|
||||
.ENDIF
|
||||
|
||||
OBJEXT = obj
|
||||
|
||||
.SUFFIXES : .obj .c
|
||||
|
||||
.INCLUDE source.mak
|
||||
|
||||
EXTRA_OBJS = argproc.obj
|
||||
|
||||
all : $(TARGET)
|
||||
! $@
|
||||
|
||||
.c.obj :
|
||||
$(CC_DEF) $(CFLAGS) $<
|
||||
|
||||
$(TARGET) : $(OBJECTS) $(EXTRA_OBJS)
|
||||
$(LD_DEF) $(LDFLAGS) /exe=$(TARGET) $+
|
||||
|
||||
clean :
|
||||
-@ if F$SEARCH("*.obj") .NES. "" then delete/noconfirm/nolog *.obj.*
|
||||
-@ if F$SEARCH("*.exe") .NES. "" then delete/noconfirm/nolog *.exe.*
|
||||
-@ if F$SEARCH("config.h") .NES. "" then delete/noconfirm/nolog config.h.*
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* $Id: e_amiga.h,v 1.1 2002/03/08 22:35:19 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Configures ctags for Amiga environment.
|
||||
*/
|
||||
#ifndef E_AMIGA_H
|
||||
#define E_AMIGA_H
|
||||
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRICMP 1
|
||||
#define HAVE_STRNICMP 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* $Id: e_djgpp.h,v 1.5 2003/10/31 04:15:35 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Configures ctags for DJGPP environment.
|
||||
*/
|
||||
#ifndef E_DJGPP_H
|
||||
#define E_DJGPP_H
|
||||
|
||||
#define CASE_INSENSITIVE_FILENAMES 1
|
||||
#define MSDOS_STYLE_PATH 1
|
||||
|
||||
#define HAVE_DIR_H 1
|
||||
#define HAVE_DIRENT_H 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_FNMATCH_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_STRING_H 1
|
||||
#define HAVE_SYS_DIR_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_SYS_TIMES_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_FNMATCH 1
|
||||
#define HAVE_MKSTEMP 1
|
||||
#define HAVE_OPENDIR 1
|
||||
#define HAVE_REGCOMP 1
|
||||
#define HAVE_REMOVE 1
|
||||
#define HAVE_SETENV 1
|
||||
#define HAVE_STAT_ST_INO 1
|
||||
#define HAVE_STRCASECMP 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRNCASECMP 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_TRUNCATE 1
|
||||
#define NEED_PROTO_LSTAT 1
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* $Id: e_mac.h,v 1.2 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2001, Maarten L. Hekkelman
|
||||
*
|
||||
* Author: Maarten L. Hekkelman <[email protected]>
|
||||
* http://www.hekkelman.com
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License. It is provided on an as-is basis and no
|
||||
* responsibility is accepted for its failure to perform as expected.
|
||||
*
|
||||
* Configures ctags for Macintosh environment.
|
||||
*/
|
||||
#ifndef E_MAC_H
|
||||
#define E_MAC_H
|
||||
|
||||
#define BUILD_MPW_TOOL 1
|
||||
|
||||
#define MACROS_USE_PATTERNS 1
|
||||
#define DEFAULT_FILE_FORMAT 2
|
||||
#define INTERNAL_SORT 1
|
||||
#define TMPDIR "/tmp"
|
||||
#define NEED_PROTO_TRUNCATE 1
|
||||
#define STDC_HEADERS 1
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_OPENDIR 1
|
||||
#define HAVE_REMOVE 1
|
||||
#define HAVE_SETENV 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_STRING_H 1
|
||||
#define HAVE_SYS_DIR_H 1
|
||||
#define HAVE_SYS_TIMES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_TYPES_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Files.h>
|
||||
|
||||
#if BUILD_MPW_TOOL
|
||||
|
||||
/*
|
||||
The following defines are collected from various header files from some
|
||||
Linux distribution
|
||||
*/
|
||||
|
||||
typedef unsigned long mode_t;
|
||||
typedef unsigned long ino_t;
|
||||
typedef unsigned long dev_t;
|
||||
typedef short nlink_t;
|
||||
typedef unsigned long uid_t;
|
||||
typedef unsigned long gid_t;
|
||||
|
||||
/* Encoding of the file mode. */
|
||||
#define S_IFMT 0170000 /* These bits determine file type. */
|
||||
|
||||
/* File types. */
|
||||
#define S_IFDIR 0040000 /* Directory. */
|
||||
#define S_IFCHR 0020000 /* Character device. */
|
||||
#define S_IFBLK 0060000 /* Block device. */
|
||||
#define S_IFREG 0100000 /* Regular file. */
|
||||
|
||||
#define S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))
|
||||
|
||||
#define S_ISDIR(mode) S_ISTYPE((mode), S_IFDIR)
|
||||
#define S_ISCHR(mode) S_ISTYPE((mode), S_IFCHR)
|
||||
#define S_ISBLK(mode) S_ISTYPE((mode), S_IFBLK)
|
||||
#define S_ISREG(mode) S_ISTYPE((mode), S_IFREG)
|
||||
|
||||
struct stat {
|
||||
dev_t st_dev; /* Device. */
|
||||
unsigned short int __pad1;
|
||||
ino_t st_ino; /* File serial number. */
|
||||
mode_t st_mode; /* File mode. */
|
||||
nlink_t st_nlink; /* Link count. */
|
||||
uid_t st_uid; /* User ID of the file's owner. */
|
||||
gid_t st_gid; /* Group ID of the file's group.*/
|
||||
off_t st_size; /* Size of file, in bytes. */
|
||||
unsigned long int st_blksize; /* Optimal block size for I/O. */
|
||||
long st_blocks; /* Number 512-byte blocks allocated. */
|
||||
time_t st_atime; /* Time of last access. */
|
||||
time_t st_mtime; /* Time of last modification. */
|
||||
time_t st_ctime; /* Time of last status change. */
|
||||
};
|
||||
|
||||
int fstat(int fildes, struct stat *buf);
|
||||
|
||||
#else
|
||||
#include <console.h>
|
||||
#include <stat.mac.h>
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
/*
|
||||
Our own stat, accepts unix like paths.
|
||||
*/
|
||||
int mstat(const char *path, struct stat *buf);
|
||||
|
||||
struct dirent {
|
||||
char d_name[64];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
FSSpec file;
|
||||
int index;
|
||||
struct dirent ent;
|
||||
} DIR;
|
||||
|
||||
extern DIR* opendir(const char *dirname);
|
||||
extern struct dirent* readdir(DIR* dirp);
|
||||
extern int closedir(DIR* dirp);
|
||||
extern void rewinddir(DIR* dirp);
|
||||
extern char* getcwd(char*, int);
|
||||
|
||||
/*
|
||||
Our own fopen, accepts unix like paths.
|
||||
*/
|
||||
extern FILE* mfopen(const char* file, const char* mode);
|
||||
|
||||
/*
|
||||
Dirty, define the standard functions fopen, stat and lstat to map to our
|
||||
own routines.
|
||||
*/
|
||||
#define fopen mfopen
|
||||
#define stat(a,b) mstat(a,b)
|
||||
#define lstat(a,b) mstat(a,b)
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* $Id: e_msoft.h,v 1.8 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Configures ctags for Microsoft environment.
|
||||
*/
|
||||
#ifndef E_MSOFT_H
|
||||
#define E_MSOFT_H
|
||||
|
||||
/* MS-DOS/Windows doesn't allow manipulation of standard error,
|
||||
* so we send it to stdout instead.
|
||||
*/
|
||||
#define errout stdout
|
||||
|
||||
#define CASE_INSENSITIVE_FILENAMES 1
|
||||
#define MANUAL_GLOBBING 1
|
||||
#define MSDOS_STYLE_PATH 1
|
||||
#define HAVE_DOS_H 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_IO_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_CHSIZE 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_STRICMP 1
|
||||
#define HAVE_STRNICMP 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_FINDNEXT 1
|
||||
#define HAVE_TEMPNAM 1
|
||||
#define tempnam(dir,pfx) _tempnam(dir,pfx)
|
||||
#define TMPDIR "\\"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
|
||||
# define HAVE_DIR_H 1
|
||||
# define HAVE_DIRENT_H 1
|
||||
# define HAVE_FINDFIRST 1
|
||||
|
||||
#elif defined (_MSC_VER)
|
||||
|
||||
# define HAVE__FINDFIRST 1
|
||||
# define HAVE_DIRECT_H 1
|
||||
|
||||
# if _MSC_VER >= 1300
|
||||
# define findfirst_t intptr_t /* Visual Studio 7 */
|
||||
# else
|
||||
# define findfirst_t long /* Visual Studio 6 or earlier */
|
||||
# endif
|
||||
|
||||
#elif defined (__MINGW32__)
|
||||
|
||||
# include <_mingw.h>
|
||||
# if defined (__MSVCRT__) && __MINGW32_MAJOR_VERSION == 1 && __MINGW32_MINOR_VERSION < 2
|
||||
/* Work-around for broken implementation of fgetpos()/fsetpos() on Mingw32 */
|
||||
# undef HAVE_FGETPOS
|
||||
# define NEED_PROTO_FGETPOS 1
|
||||
# endif
|
||||
# define HAVE_DIR_H 1
|
||||
# define HAVE_DIRENT_H 1
|
||||
# define HAVE__FINDFIRST 1
|
||||
# define ffblk _finddata_t
|
||||
# define FA_DIREC _A_SUBDIR
|
||||
# define ff_name name
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* $Id: e_os2.h,v 1.1 2002/03/08 22:35:19 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Configures ctags for OS/2 environment.
|
||||
*/
|
||||
#ifndef E_OS2_H
|
||||
#define E_OS2_H
|
||||
|
||||
#define UNIX_PATH_SEPARATOR 1
|
||||
#define CASE_INSENSITIVE_FILENAMES 1
|
||||
#define HAVE_DIRENT_H 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_IO_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_CHSIZE 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_FTRUNCATE 1
|
||||
#define HAVE_OPENDIR 1
|
||||
#define HAVE_REGCOMP 1
|
||||
#define HAVE_REMOVE 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRICMP 1
|
||||
#define HAVE_STRNICMP 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_TRUNCATE 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* $Id: e_qdos.h,v 1.1 2002/03/08 22:35:19 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Configures ctags for QDOS environment.
|
||||
*/
|
||||
#ifndef E_QDOS_H
|
||||
#define E_QDOS_H
|
||||
|
||||
#define HAVE_DIRENT_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_SYS_TIMES_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define STDC_HEADERS 1
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_FTRUNCATE 1
|
||||
#define HAVE_OPENDIR 1
|
||||
#define HAVE_PUTENV 1
|
||||
#define HAVE_REMOVE 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_TIMES 1
|
||||
#define HAVE_TRUNCATE 1
|
||||
#define NON_CONST_PUTENV_PROTOTYPE 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* $Id: e_riscos.h,v 1.1 2002/03/08 22:35:19 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Andrew Wingate
|
||||
*
|
||||
* Author: Andrew Wingate <[email protected]>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License. It is provided on an as-is basis and no
|
||||
* responsibility is accepted for its failure to perform as expected.
|
||||
*
|
||||
* Configures ctags for RISC OS environment.
|
||||
*
|
||||
* We currently use UnixLib. This file is subject to change if
|
||||
* we move to using SharedCLibrary and libGNU.
|
||||
*/
|
||||
#ifndef E_RISCOS_H
|
||||
#define E_RISCOS_H
|
||||
|
||||
#define MACROS_USE_PATTERNS 1
|
||||
#define DEFAULT_FILE_FORMAT 2
|
||||
#define INTERNAL_SORT 1 /* Not all systems will have installed sort(1) */
|
||||
#define TMPDIR "<Wimp$ScrapDir>"
|
||||
|
||||
/* Various definitions for UnixLib */
|
||||
#define STDC_HEADERS 1
|
||||
#define HAVE_CHMOD 1
|
||||
#define HAVE_CHSIZE 1
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_FNMATCH 1
|
||||
#define HAVE_FTRUNCATE 1
|
||||
#define HAVE_MKSTEMP 1
|
||||
#define HAVE_OPENDIR 1
|
||||
#define HAVE_PUTENV 1
|
||||
#define HAVE_REGCOMP 1 /* Requires RegEx library */
|
||||
#define HAVE_REMOVE 1
|
||||
#define HAVE_SETENV 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRICMP 1
|
||||
#define HAVE_STRNICMP 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_TIMES 1
|
||||
#define HAVE_TRUNCATE 1
|
||||
#define HAVE_DIRENT_H 1
|
||||
#define HAVE_FCNTL_H 1
|
||||
#define HAVE_FNMATCH_H 1
|
||||
#define HAVE_STAT_H 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_STRING_H 1
|
||||
#define HAVE_SYS_DIR_H 1
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#define HAVE_SYS_TIMES_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* $Id: e_vms.h,v 1.1 2002/03/08 22:35:19 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Configures ctags for VMS environment.
|
||||
*/
|
||||
#ifndef E_VMS_H
|
||||
#define E_VMS_H
|
||||
|
||||
#define CASE_INSENSITIVE_FILENAMES 1
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_TIME_H 1
|
||||
#ifdef VAXC
|
||||
# define HAVE_STAT_H 1
|
||||
# define HAVE_TYPES_H 1
|
||||
#else
|
||||
# define HAVE_FCNTL_H 1
|
||||
# define HAVE_SYS_STAT_H 1
|
||||
# define HAVE_SYS_TYPES_H 1
|
||||
#endif
|
||||
#define HAVE_CLOCK 1
|
||||
#define HAVE_FGETPOS 1
|
||||
#define HAVE_STRERROR 1
|
||||
#define HAVE_STRSTR 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,847 @@
|
||||
/*
|
||||
* $Id: entry.c,v 1.11 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for creating tag entries.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h> /* to define isspace () */
|
||||
#include <errno.h>
|
||||
|
||||
#if defined (HAVE_SYS_TYPES_H)
|
||||
# include <sys/types.h> /* to declare off_t on some hosts */
|
||||
#endif
|
||||
#if defined (HAVE_TYPES_H)
|
||||
# include <types.h> /* to declare off_t on some hosts */
|
||||
#endif
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h> /* to declare close (), ftruncate (), truncate () */
|
||||
#endif
|
||||
|
||||
/* These header files provide for the functions necessary to do file
|
||||
* truncation.
|
||||
*/
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "ctags.h"
|
||||
#include "entry.h"
|
||||
#include "main.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "sort.h"
|
||||
#include "strlist.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define PSEUDO_TAG_PREFIX "!_"
|
||||
|
||||
#define includeExtensionFlags() (Option.tagFileFormat > 1)
|
||||
|
||||
/*
|
||||
* Portability defines
|
||||
*/
|
||||
#if !defined(HAVE_TRUNCATE) && !defined(HAVE_FTRUNCATE) && !defined(HAVE_CHSIZE)
|
||||
# define USE_REPLACEMENT_TRUNCATE
|
||||
#endif
|
||||
|
||||
/* Hack for rediculous practice of Microsoft Visual C++.
|
||||
*/
|
||||
#if defined (WIN32) && defined (_MSC_VER)
|
||||
# define chsize _chsize
|
||||
# define open _open
|
||||
# define close _close
|
||||
# define O_RDWR _O_RDWR
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
|
||||
tagFile TagFile = {
|
||||
NULL, /* tag file name */
|
||||
NULL, /* tag file directory (absolute) */
|
||||
NULL, /* file pointer */
|
||||
{ 0, 0 }, /* numTags */
|
||||
{ 0, 0, 0 }, /* max */
|
||||
{ NULL, NULL, 0 }, /* etags */
|
||||
NULL /* vLine */
|
||||
};
|
||||
|
||||
static boolean TagsToStdout = FALSE;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
#ifdef NEED_PROTO_TRUNCATE
|
||||
extern int truncate (const char *path, off_t length);
|
||||
#endif
|
||||
|
||||
#ifdef NEED_PROTO_FTRUNCATE
|
||||
extern int ftruncate (int fd, off_t length);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern void freeTagFileResources (void)
|
||||
{
|
||||
if (TagFile.directory != NULL)
|
||||
eFree (TagFile.directory);
|
||||
vStringDelete (TagFile.vLine);
|
||||
}
|
||||
|
||||
extern const char *tagFileName (void)
|
||||
{
|
||||
return TagFile.name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pseudo tag support
|
||||
*/
|
||||
|
||||
static void rememberMaxLengths (const size_t nameLength, const size_t lineLength)
|
||||
{
|
||||
if (nameLength > TagFile.max.tag)
|
||||
TagFile.max.tag = nameLength;
|
||||
|
||||
if (lineLength > TagFile.max.line)
|
||||
TagFile.max.line = lineLength;
|
||||
}
|
||||
|
||||
static void writePseudoTag (
|
||||
const char *const tagName,
|
||||
const char *const fileName,
|
||||
const char *const pattern)
|
||||
{
|
||||
const int length = fprintf (
|
||||
TagFile.fp, "%s%s\t%s\t/%s/\n",
|
||||
PSEUDO_TAG_PREFIX, tagName, fileName, pattern);
|
||||
++TagFile.numTags.added;
|
||||
rememberMaxLengths (strlen (tagName), (size_t) length);
|
||||
}
|
||||
|
||||
static void addPseudoTags (void)
|
||||
{
|
||||
if (! Option.xref)
|
||||
{
|
||||
char format [11];
|
||||
const char *formatComment = "unknown format";
|
||||
|
||||
sprintf (format, "%u", Option.tagFileFormat);
|
||||
|
||||
if (Option.tagFileFormat == 1)
|
||||
formatComment = "original ctags format";
|
||||
else if (Option.tagFileFormat == 2)
|
||||
formatComment =
|
||||
"extended format; --format=1 will not append ;\" to lines";
|
||||
|
||||
writePseudoTag ("TAG_FILE_FORMAT", format, formatComment);
|
||||
writePseudoTag ("TAG_FILE_SORTED",
|
||||
Option.sorted == SO_FOLDSORTED ? "2" :
|
||||
(Option.sorted == SO_SORTED ? "1" : "0"),
|
||||
"0=unsorted, 1=sorted, 2=foldcase");
|
||||
writePseudoTag ("TAG_PROGRAM_AUTHOR", AUTHOR_NAME, AUTHOR_EMAIL);
|
||||
writePseudoTag ("TAG_PROGRAM_NAME", PROGRAM_NAME, "");
|
||||
writePseudoTag ("TAG_PROGRAM_URL", PROGRAM_URL, "official site");
|
||||
writePseudoTag ("TAG_PROGRAM_VERSION", PROGRAM_VERSION, "");
|
||||
}
|
||||
}
|
||||
|
||||
static void updateSortedFlag (
|
||||
const char *const line, FILE *const fp, fpos_t startOfLine)
|
||||
{
|
||||
const char *const tab = strchr (line, '\t');
|
||||
|
||||
if (tab != NULL)
|
||||
{
|
||||
const long boolOffset = tab - line + 1; /* where it should be */
|
||||
|
||||
if (line [boolOffset] == '0' || line [boolOffset] == '1')
|
||||
{
|
||||
fpos_t nextLine;
|
||||
|
||||
if (fgetpos (fp, &nextLine) == -1 || fsetpos (fp, &startOfLine) == -1)
|
||||
error (WARNING, "Failed to update 'sorted' pseudo-tag");
|
||||
else
|
||||
{
|
||||
fpos_t flagLocation;
|
||||
int c, d;
|
||||
|
||||
do
|
||||
c = fgetc (fp);
|
||||
while (c != '\t' && c != '\n');
|
||||
fgetpos (fp, &flagLocation);
|
||||
d = fgetc (fp);
|
||||
if (c == '\t' && (d == '0' || d == '1') &&
|
||||
d != (int) Option.sorted)
|
||||
{
|
||||
fsetpos (fp, &flagLocation);
|
||||
fputc (Option.sorted == SO_FOLDSORTED ? '2' :
|
||||
(Option.sorted == SO_SORTED ? '1' : '0'), fp);
|
||||
}
|
||||
fsetpos (fp, &nextLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Look through all line beginning with "!_TAG_FILE", and update those which
|
||||
* require it.
|
||||
*/
|
||||
static long unsigned int updatePseudoTags (FILE *const fp)
|
||||
{
|
||||
enum { maxEntryLength = 20 };
|
||||
char entry [maxEntryLength + 1];
|
||||
unsigned long linesRead = 0;
|
||||
fpos_t startOfLine;
|
||||
size_t entryLength;
|
||||
const char *line;
|
||||
|
||||
sprintf (entry, "%sTAG_FILE", PSEUDO_TAG_PREFIX);
|
||||
entryLength = strlen (entry);
|
||||
Assert (entryLength < maxEntryLength);
|
||||
|
||||
fgetpos (fp, &startOfLine);
|
||||
line = readLine (TagFile.vLine, fp);
|
||||
while (line != NULL && line [0] == entry [0])
|
||||
{
|
||||
++linesRead;
|
||||
if (strncmp (line, entry, entryLength) == 0)
|
||||
{
|
||||
char tab, classType [16];
|
||||
|
||||
if (sscanf (line + entryLength, "%15s%c", classType, &tab) == 2 &&
|
||||
tab == '\t')
|
||||
{
|
||||
if (strcmp (classType, "_SORTED") == 0)
|
||||
updateSortedFlag (line, fp, startOfLine);
|
||||
}
|
||||
fgetpos (fp, &startOfLine);
|
||||
}
|
||||
line = readLine (TagFile.vLine, fp);
|
||||
}
|
||||
while (line != NULL) /* skip to end of file */
|
||||
{
|
||||
++linesRead;
|
||||
line = readLine (TagFile.vLine, fp);
|
||||
}
|
||||
return linesRead;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tag file management
|
||||
*/
|
||||
|
||||
static boolean isValidTagAddress (const char *const excmd)
|
||||
{
|
||||
boolean isValid = FALSE;
|
||||
|
||||
if (strchr ("/?", excmd [0]) != NULL)
|
||||
isValid = TRUE;
|
||||
else
|
||||
{
|
||||
char *address = xMalloc (strlen (excmd) + 1, char);
|
||||
if (sscanf (excmd, "%[^;\n]", address) == 1 &&
|
||||
strspn (address,"0123456789") == strlen (address))
|
||||
isValid = TRUE;
|
||||
eFree (address);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
static boolean isCtagsLine (const char *const line)
|
||||
{
|
||||
enum fieldList { TAG, TAB1, SRC_FILE, TAB2, EXCMD, NUM_FIELDS };
|
||||
boolean ok = FALSE; /* we assume not unless confirmed */
|
||||
const size_t fieldLength = strlen (line) + 1;
|
||||
char *const fields = xMalloc (NUM_FIELDS * fieldLength, char);
|
||||
|
||||
if (fields == NULL)
|
||||
error (FATAL, "Cannot analyze tag file");
|
||||
else
|
||||
{
|
||||
#define field(x) (fields + ((size_t) (x) * fieldLength))
|
||||
|
||||
const int numFields = sscanf (
|
||||
line, "%[^\t]%[\t]%[^\t]%[\t]%[^\r\n]",
|
||||
field (TAG), field (TAB1), field (SRC_FILE),
|
||||
field (TAB2), field (EXCMD));
|
||||
|
||||
/* There must be exactly five fields: two tab fields containing
|
||||
* exactly one tab each, the tag must not begin with "#", and the
|
||||
* file name should not end with ";", and the excmd must be
|
||||
* accceptable.
|
||||
*
|
||||
* These conditions will reject tag-looking lines like:
|
||||
* int a; <C-comment>
|
||||
* #define LABEL <C-comment>
|
||||
*/
|
||||
if (numFields == NUM_FIELDS &&
|
||||
strlen (field (TAB1)) == 1 &&
|
||||
strlen (field (TAB2)) == 1 &&
|
||||
field (TAG) [0] != '#' &&
|
||||
field (SRC_FILE) [strlen (field (SRC_FILE)) - 1] != ';' &&
|
||||
isValidTagAddress (field (EXCMD)))
|
||||
ok = TRUE;
|
||||
|
||||
eFree (fields);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static boolean isEtagsLine (const char *const line)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
if (line [0] == '\f')
|
||||
result = (boolean) (line [1] == '\n' || line [1] == '\r');
|
||||
return result;
|
||||
}
|
||||
|
||||
static boolean isTagFile (const char *const filename)
|
||||
{
|
||||
boolean ok = FALSE; /* we assume not unless confirmed */
|
||||
FILE *const fp = fopen (filename, "rb");
|
||||
|
||||
if (fp == NULL && errno == ENOENT)
|
||||
ok = TRUE;
|
||||
else if (fp != NULL)
|
||||
{
|
||||
const char *line = readLine (TagFile.vLine, fp);
|
||||
|
||||
if (line == NULL)
|
||||
ok = TRUE;
|
||||
else
|
||||
ok = (boolean) (isCtagsLine (line) || isEtagsLine (line));
|
||||
fclose (fp);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
extern void copyBytes (FILE* const fromFp, FILE* const toFp, const long size)
|
||||
{
|
||||
enum { BufferSize = 1000 };
|
||||
long toRead, numRead;
|
||||
char* buffer = xMalloc (BufferSize, char);
|
||||
long remaining = size;
|
||||
do
|
||||
{
|
||||
toRead = (0 < remaining && remaining < BufferSize) ?
|
||||
remaining : (long) BufferSize;
|
||||
numRead = fread (buffer, (size_t) 1, (size_t) toRead, fromFp);
|
||||
if (fwrite (buffer, (size_t)1, (size_t)numRead, toFp) < (size_t)numRead)
|
||||
error (FATAL | PERROR, "cannot complete write");
|
||||
if (remaining > 0)
|
||||
remaining -= numRead;
|
||||
} while (numRead == toRead && remaining != 0);
|
||||
eFree (buffer);
|
||||
}
|
||||
|
||||
extern void copyFile (const char *const from, const char *const to, const long size)
|
||||
{
|
||||
FILE* const fromFp = fopen (from, "rb");
|
||||
if (fromFp == NULL)
|
||||
error (FATAL | PERROR, "cannot open file to copy");
|
||||
else
|
||||
{
|
||||
FILE* const toFp = fopen (to, "wb");
|
||||
if (toFp == NULL)
|
||||
error (FATAL | PERROR, "cannot open copy destination");
|
||||
else
|
||||
{
|
||||
copyBytes (fromFp, toFp, size);
|
||||
fclose (toFp);
|
||||
}
|
||||
fclose (fromFp);
|
||||
}
|
||||
}
|
||||
|
||||
extern void openTagFile (void)
|
||||
{
|
||||
setDefaultTagFileName ();
|
||||
TagsToStdout = isDestinationStdout ();
|
||||
|
||||
if (TagFile.vLine == NULL)
|
||||
TagFile.vLine = vStringNew ();
|
||||
|
||||
/* Open the tags file.
|
||||
*/
|
||||
if (TagsToStdout)
|
||||
TagFile.fp = tempFile ("w", &TagFile.name);
|
||||
else
|
||||
{
|
||||
boolean fileExists;
|
||||
|
||||
setDefaultTagFileName ();
|
||||
TagFile.name = eStrdup (Option.tagFileName);
|
||||
fileExists = doesFileExist (TagFile.name);
|
||||
if (fileExists && ! isTagFile (TagFile.name))
|
||||
error (FATAL,
|
||||
"\"%s\" doesn't look like a tag file; I refuse to overwrite it.",
|
||||
TagFile.name);
|
||||
|
||||
if (Option.etags)
|
||||
{
|
||||
if (Option.append && fileExists)
|
||||
TagFile.fp = fopen (TagFile.name, "a+b");
|
||||
else
|
||||
TagFile.fp = fopen (TagFile.name, "w+b");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Option.append && fileExists)
|
||||
{
|
||||
TagFile.fp = fopen (TagFile.name, "r+");
|
||||
if (TagFile.fp != NULL)
|
||||
{
|
||||
TagFile.numTags.prev = updatePseudoTags (TagFile.fp);
|
||||
fclose (TagFile.fp);
|
||||
TagFile.fp = fopen (TagFile.name, "a+");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TagFile.fp = fopen (TagFile.name, "w");
|
||||
if (TagFile.fp != NULL)
|
||||
addPseudoTags ();
|
||||
}
|
||||
}
|
||||
if (TagFile.fp == NULL)
|
||||
{
|
||||
error (FATAL | PERROR, "cannot open tag file");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
if (TagsToStdout)
|
||||
TagFile.directory = eStrdup (CurrentDirectory);
|
||||
else
|
||||
TagFile.directory = absoluteDirname (TagFile.name);
|
||||
}
|
||||
|
||||
#ifdef USE_REPLACEMENT_TRUNCATE
|
||||
|
||||
/* Replacement for missing library function.
|
||||
*/
|
||||
static int replacementTruncate (const char *const name, const long size)
|
||||
{
|
||||
char *tempName = NULL;
|
||||
FILE *fp = tempFile ("w", &tempName);
|
||||
fclose (fp);
|
||||
copyFile (name, tempName, size);
|
||||
copyFile (tempName, name, WHOLE_FILE);
|
||||
remove (tempName);
|
||||
eFree (tempName);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void sortTagFile (void)
|
||||
{
|
||||
if (TagFile.numTags.added > 0L)
|
||||
{
|
||||
if (Option.sorted != SO_UNSORTED)
|
||||
{
|
||||
verbose ("sorting tag file\n");
|
||||
#ifdef EXTERNAL_SORT
|
||||
externalSortTags (TagsToStdout);
|
||||
#else
|
||||
internalSortTags (TagsToStdout);
|
||||
#endif
|
||||
}
|
||||
else if (TagsToStdout)
|
||||
catFile (tagFileName ());
|
||||
}
|
||||
if (TagsToStdout)
|
||||
remove (tagFileName ()); /* remove temporary file */
|
||||
}
|
||||
|
||||
static void resizeTagFile (const long newSize)
|
||||
{
|
||||
int result;
|
||||
|
||||
#ifdef USE_REPLACEMENT_TRUNCATE
|
||||
result = replacementTruncate (TagFile.name, newSize);
|
||||
#else
|
||||
# ifdef HAVE_TRUNCATE
|
||||
result = truncate (TagFile.name, (off_t) newSize);
|
||||
# else
|
||||
const int fd = open (TagFile.name, O_RDWR);
|
||||
|
||||
if (fd == -1)
|
||||
result = -1;
|
||||
else
|
||||
{
|
||||
# ifdef HAVE_FTRUNCATE
|
||||
result = ftruncate (fd, (off_t) newSize);
|
||||
# else
|
||||
# ifdef HAVE_CHSIZE
|
||||
result = chsize (fd, newSize);
|
||||
# endif
|
||||
# endif
|
||||
close (fd);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
if (result == -1)
|
||||
fprintf (errout, "Cannot shorten tag file: errno = %d\n", errno);
|
||||
}
|
||||
|
||||
static void writeEtagsIncludes (FILE *const fp)
|
||||
{
|
||||
if (Option.etagsInclude)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < stringListCount (Option.etagsInclude) ; ++i)
|
||||
{
|
||||
vString *item = stringListItem (Option.etagsInclude, i);
|
||||
fprintf (fp, "\f\n%s,include\n", vStringValue (item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void closeTagFile (const boolean resize)
|
||||
{
|
||||
long desiredSize, size;
|
||||
|
||||
if (Option.etags)
|
||||
writeEtagsIncludes (TagFile.fp);
|
||||
desiredSize = ftell (TagFile.fp);
|
||||
fseek (TagFile.fp, 0L, SEEK_END);
|
||||
size = ftell (TagFile.fp);
|
||||
fclose (TagFile.fp);
|
||||
if (resize && desiredSize < size)
|
||||
{
|
||||
DebugStatement (
|
||||
debugPrintf (DEBUG_STATUS, "shrinking %s from %ld to %ld bytes\n",
|
||||
TagFile.name, size, desiredSize); )
|
||||
resizeTagFile (desiredSize);
|
||||
}
|
||||
sortTagFile ();
|
||||
eFree (TagFile.name);
|
||||
TagFile.name = NULL;
|
||||
}
|
||||
|
||||
extern void beginEtagsFile (void)
|
||||
{
|
||||
TagFile.etags.fp = tempFile ("w+b", &TagFile.etags.name);
|
||||
TagFile.etags.byteCount = 0;
|
||||
}
|
||||
|
||||
extern void endEtagsFile (const char *const name)
|
||||
{
|
||||
const char *line;
|
||||
|
||||
fprintf (TagFile.fp, "\f\n%s,%ld\n", name, (long) TagFile.etags.byteCount);
|
||||
if (TagFile.etags.fp != NULL)
|
||||
{
|
||||
rewind (TagFile.etags.fp);
|
||||
while ((line = readLine (TagFile.vLine, TagFile.etags.fp)) != NULL)
|
||||
fputs (line, TagFile.fp);
|
||||
fclose (TagFile.etags.fp);
|
||||
remove (TagFile.etags.name);
|
||||
eFree (TagFile.etags.name);
|
||||
TagFile.etags.fp = NULL;
|
||||
TagFile.etags.name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tag entry management
|
||||
*/
|
||||
|
||||
/* This function copies the current line out to a specified file. It has no
|
||||
* effect on the fileGetc () function. During copying, any '\' characters
|
||||
* are doubled and a leading '^' or trailing '$' is also quoted. End of line
|
||||
* characters (line feed or carriage return) are dropped.
|
||||
*/
|
||||
static size_t writeSourceLine (FILE *const fp, const char *const line)
|
||||
{
|
||||
size_t length = 0;
|
||||
const char *p;
|
||||
|
||||
/* Write everything up to, but not including, a line end character.
|
||||
*/
|
||||
for (p = line ; *p != '\0' ; ++p)
|
||||
{
|
||||
const int next = *(p + 1);
|
||||
const int c = *p;
|
||||
|
||||
if (c == CRETURN || c == NEWLINE)
|
||||
break;
|
||||
|
||||
/* If character is '\', or a terminal '$', then quote it.
|
||||
*/
|
||||
if (c == BACKSLASH || c == (Option.backward ? '?' : '/') ||
|
||||
(c == '$' && (next == NEWLINE || next == CRETURN)))
|
||||
{
|
||||
putc (BACKSLASH, fp);
|
||||
++length;
|
||||
}
|
||||
putc (c, fp);
|
||||
++length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Writes "line", stripping leading and duplicate white space.
|
||||
*/
|
||||
static size_t writeCompactSourceLine (FILE *const fp, const char *const line)
|
||||
{
|
||||
boolean lineStarted = FALSE;
|
||||
size_t length = 0;
|
||||
const char *p;
|
||||
int c;
|
||||
|
||||
/* Write everything up to, but not including, the newline.
|
||||
*/
|
||||
for (p = line, c = *p ; c != NEWLINE && c != '\0' ; c = *++p)
|
||||
{
|
||||
if (lineStarted || ! isspace (c)) /* ignore leading spaces */
|
||||
{
|
||||
lineStarted = TRUE;
|
||||
if (isspace (c))
|
||||
{
|
||||
int next;
|
||||
|
||||
/* Consume repeating white space.
|
||||
*/
|
||||
while (next = *(p+1) , isspace (next) && next != NEWLINE)
|
||||
++p;
|
||||
c = ' '; /* force space character for any white space */
|
||||
}
|
||||
if (c != CRETURN || *(p + 1) != NEWLINE)
|
||||
{
|
||||
putc (c, fp);
|
||||
++length;
|
||||
}
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static int writeXrefEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
const char *const line =
|
||||
readSourceLine (TagFile.vLine, tag->filePosition, NULL);
|
||||
int length;
|
||||
|
||||
if (Option.tagFileFormat == 1)
|
||||
length = fprintf (TagFile.fp, "%-16s %4lu %-16s ", tag->name,
|
||||
tag->lineNumber, tag->sourceFileName);
|
||||
else
|
||||
length = fprintf (TagFile.fp, "%-16s %-10s %4lu %-16s ", tag->name,
|
||||
tag->kindName, tag->lineNumber, tag->sourceFileName);
|
||||
|
||||
length += writeCompactSourceLine (TagFile.fp, line);
|
||||
putc (NEWLINE, TagFile.fp);
|
||||
++length;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Truncates the text line containing the tag at the character following the
|
||||
* tag, providing a character which designates the end of the tag.
|
||||
*/
|
||||
static void truncateTagLine (
|
||||
char *const line, const char *const token, const boolean discardNewline)
|
||||
{
|
||||
char *p = strstr (line, token);
|
||||
|
||||
if (p != NULL)
|
||||
{
|
||||
p += strlen (token);
|
||||
if (*p != '\0' && ! (*p == '\n' && discardNewline))
|
||||
++p; /* skip past character terminating character */
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int writeEtagsEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
int length;
|
||||
|
||||
if (tag->isFileEntry)
|
||||
length = fprintf (TagFile.etags.fp, "\177%s\001%lu,0\n",
|
||||
tag->name, tag->lineNumber);
|
||||
else
|
||||
{
|
||||
long seekValue;
|
||||
char *const line =
|
||||
readSourceLine (TagFile.vLine, tag->filePosition, &seekValue);
|
||||
|
||||
if (tag->truncateLine)
|
||||
truncateTagLine (line, tag->name, TRUE);
|
||||
else
|
||||
line [strlen (line) - 1] = '\0';
|
||||
|
||||
length = fprintf (TagFile.etags.fp, "%s\177%s\001%lu,%ld\n", line,
|
||||
tag->name, tag->lineNumber, seekValue);
|
||||
}
|
||||
TagFile.etags.byteCount += length;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int addExtensionFields (const tagEntryInfo *const tag)
|
||||
{
|
||||
const char* const kindKey = Option.extensionFields.kindKey ? "kind:" : "";
|
||||
boolean first = TRUE;
|
||||
const char* separator = ";\"";
|
||||
const char* const empty = "";
|
||||
int length = 0;
|
||||
/* "sep" returns a value only the first time it is evaluated */
|
||||
#define sep (first ? (first = FALSE, separator) : empty)
|
||||
|
||||
if (tag->kindName != NULL && (Option.extensionFields.kindLong ||
|
||||
(Option.extensionFields.kind && tag->kind == '\0')))
|
||||
length += fprintf (TagFile.fp,"%s\t%s%s", sep, kindKey, tag->kindName);
|
||||
else if (tag->kind != '\0' && (Option.extensionFields.kind ||
|
||||
(Option.extensionFields.kindLong && tag->kindName == NULL)))
|
||||
length += fprintf (TagFile.fp, "%s\t%s%c", sep, kindKey, tag->kind);
|
||||
|
||||
if (Option.extensionFields.lineNumber)
|
||||
length += fprintf (TagFile.fp, "%s\tline:%ld", sep, tag->lineNumber);
|
||||
|
||||
if (Option.extensionFields.language && tag->language != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\tlanguage:%s", sep, tag->language);
|
||||
|
||||
if (Option.extensionFields.scope &&
|
||||
tag->extensionFields.scope [0] != NULL &&
|
||||
tag->extensionFields.scope [1] != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\t%s:%s", sep,
|
||||
tag->extensionFields.scope [0],
|
||||
tag->extensionFields.scope [1]);
|
||||
|
||||
if (Option.extensionFields.typeRef &&
|
||||
tag->extensionFields.typeRef [0] != NULL &&
|
||||
tag->extensionFields.typeRef [1] != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\ttyperef:%s:%s", sep,
|
||||
tag->extensionFields.typeRef [0],
|
||||
tag->extensionFields.typeRef [1]);
|
||||
|
||||
if (Option.extensionFields.fileScope && tag->isFileScope)
|
||||
length += fprintf (TagFile.fp, "%s\tfile:", sep);
|
||||
|
||||
if (Option.extensionFields.inheritance &&
|
||||
tag->extensionFields.inheritance != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\tinherits:%s", sep,
|
||||
tag->extensionFields.inheritance);
|
||||
|
||||
if (Option.extensionFields.access && tag->extensionFields.access != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\taccess:%s", sep,
|
||||
tag->extensionFields.access);
|
||||
|
||||
if (Option.extensionFields.implementation &&
|
||||
tag->extensionFields.implementation != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\timplementation:%s", sep,
|
||||
tag->extensionFields.implementation);
|
||||
|
||||
if (Option.extensionFields.signature &&
|
||||
tag->extensionFields.signature != NULL)
|
||||
length += fprintf (TagFile.fp, "%s\tsignature:%s", sep,
|
||||
tag->extensionFields.signature);
|
||||
|
||||
return length;
|
||||
#undef sep
|
||||
}
|
||||
|
||||
static int writePatternEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
char *const line = readSourceLine (TagFile.vLine, tag->filePosition, NULL);
|
||||
const int searchChar = Option.backward ? '?' : '/';
|
||||
boolean newlineTerminated;
|
||||
int length = 0;
|
||||
|
||||
if (tag->truncateLine)
|
||||
truncateTagLine (line, tag->name, FALSE);
|
||||
newlineTerminated = (boolean) (line [strlen (line) - 1] == '\n');
|
||||
|
||||
length += fprintf (TagFile.fp, "%c^", searchChar);
|
||||
length += writeSourceLine (TagFile.fp, line);
|
||||
length += fprintf (TagFile.fp, "%s%c", newlineTerminated ? "$":"", searchChar);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int writeLineNumberEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
return fprintf (TagFile.fp, "%lu", tag->lineNumber);
|
||||
}
|
||||
|
||||
static int writeCtagsEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
int length = fprintf (TagFile.fp, "%s\t%s\t",
|
||||
tag->name, tag->sourceFileName);
|
||||
|
||||
if (tag->lineNumberEntry)
|
||||
length += writeLineNumberEntry (tag);
|
||||
else
|
||||
length += writePatternEntry (tag);
|
||||
|
||||
if (includeExtensionFlags ())
|
||||
length += addExtensionFields (tag);
|
||||
|
||||
length += fprintf (TagFile.fp, "\n");
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
extern void makeTagEntry (const tagEntryInfo *const tag)
|
||||
{
|
||||
Assert (tag->name != NULL);
|
||||
if (tag->name [0] == '\0')
|
||||
error (WARNING, "ignoring null tag in %s", vStringValue (File.name));
|
||||
else
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
DebugStatement ( debugEntry (tag); )
|
||||
if (Option.xref)
|
||||
{
|
||||
if (! tag->isFileEntry)
|
||||
length = writeXrefEntry (tag);
|
||||
}
|
||||
else if (Option.etags)
|
||||
length = writeEtagsEntry (tag);
|
||||
else
|
||||
length = writeCtagsEntry (tag);
|
||||
|
||||
++TagFile.numTags.added;
|
||||
rememberMaxLengths (strlen (tag->name), (size_t) length);
|
||||
DebugStatement ( fflush (TagFile.fp); )
|
||||
}
|
||||
}
|
||||
|
||||
extern void initTagEntry (tagEntryInfo *const e, const char *const name)
|
||||
{
|
||||
Assert (File.source.name != NULL);
|
||||
memset (e, 0, sizeof (tagEntryInfo));
|
||||
e->lineNumberEntry = (boolean) (Option.locate == EX_LINENUM);
|
||||
e->lineNumber = getSourceLineNumber ();
|
||||
e->language = getSourceLanguageName ();
|
||||
e->filePosition = getInputFilePosition ();
|
||||
e->sourceFileName = getSourceFileTagPath ();
|
||||
e->name = name;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* $Id: entry.h,v 1.5 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to entry.c
|
||||
*/
|
||||
#ifndef _ENTRY_H
|
||||
#define _ENTRY_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define WHOLE_FILE -1L
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
/* Maintains the state of the tag file.
|
||||
*/
|
||||
typedef struct eTagFile {
|
||||
char *name;
|
||||
char *directory;
|
||||
FILE *fp;
|
||||
struct sNumTags { unsigned long added, prev; } numTags;
|
||||
struct sMax { size_t line, tag, file; } max;
|
||||
struct sEtags {
|
||||
char *name;
|
||||
FILE *fp;
|
||||
size_t byteCount;
|
||||
} etags;
|
||||
vString *vLine;
|
||||
} tagFile;
|
||||
|
||||
typedef struct sTagFields {
|
||||
unsigned int count; /* number of additional extension flags */
|
||||
const char *const *label; /* list of labels for extension flags */
|
||||
const char *const *value; /* list of values for extension flags */
|
||||
} tagFields;
|
||||
|
||||
/* Information about the current tag candidate.
|
||||
*/
|
||||
typedef struct sTagEntryInfo {
|
||||
boolean lineNumberEntry; /* pattern or line number entry */
|
||||
unsigned long lineNumber; /* line number of tag */
|
||||
fpos_t filePosition; /* file position of line containing tag */
|
||||
const char* language; /* language of source file */
|
||||
boolean isFileScope; /* is tag visibile only within source file? */
|
||||
boolean isFileEntry; /* is this just an entry for a file name? */
|
||||
boolean truncateLine; /* truncate tag line at end of tag name? */
|
||||
const char *sourceFileName; /* name of source file */
|
||||
const char *name; /* name of the tag */
|
||||
const char *kindName; /* kind of tag */
|
||||
char kind; /* single character representation of kind */
|
||||
struct {
|
||||
const char* access;
|
||||
const char* fileScope;
|
||||
const char* implementation;
|
||||
const char* inheritance;
|
||||
const char* scope [2]; /* value and key */
|
||||
const char* signature;
|
||||
|
||||
/* type (union/struct/etc.) and name for a variable or typedef. */
|
||||
const char* typeRef [2]; /* e.g., "struct" and struct name */
|
||||
|
||||
} extensionFields; /* list of extension fields*/
|
||||
} tagEntryInfo;
|
||||
|
||||
/*
|
||||
* GLOBAL VARIABLES
|
||||
*/
|
||||
extern tagFile TagFile;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void freeTagFileResources (void);
|
||||
extern const char *tagFileName (void);
|
||||
extern void copyBytes (FILE* const fromFp, FILE* const toFp, const long size);
|
||||
extern void copyFile (const char *const from, const char *const to, const long size);
|
||||
extern void openTagFile (void);
|
||||
extern void closeTagFile (const boolean resize);
|
||||
extern void beginEtagsFile (void);
|
||||
extern void endEtagsFile (const char *const name);
|
||||
extern void makeTagEntry (const tagEntryInfo *const tag);
|
||||
extern void initTagEntry (tagEntryInfo *const e, const char *const name);
|
||||
|
||||
#endif /* _ENTRY_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* $Id: erlang.c,v 1.3 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2003, Brent Fulgham <[email protected]>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for Erlang language
|
||||
* files. Some of the parsing constructs are based on the Emacs 'etags'
|
||||
* program by Francesco Potori <[email protected]>
|
||||
*/
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "entry.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_MACRO, K_FUNCTION, K_MODULE, K_RECORD
|
||||
} erlangKind;
|
||||
|
||||
static kindOption ErlangKinds[] = {
|
||||
{TRUE, 'd', "macro", "macro definitions"},
|
||||
{TRUE, 'f', "function", "functions"},
|
||||
{TRUE, 'm', "module", "modules"},
|
||||
{TRUE, 'r', "record", "record definitions"},
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
/* tagEntryInfo and vString should be preinitialized/preallocated but not
|
||||
* necessary. If successful you will find class name in vString
|
||||
*/
|
||||
|
||||
static boolean isIdentifierFirstCharacter (int c)
|
||||
{
|
||||
return (boolean) (isalpha (c));
|
||||
}
|
||||
|
||||
static boolean isIdentifierCharacter (int c)
|
||||
{
|
||||
return (boolean) (isalnum (c) || c == '_' || c == ':');
|
||||
}
|
||||
|
||||
static const unsigned char *skipSpace (const unsigned char *cp)
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
static const unsigned char *parseIdentifier (
|
||||
const unsigned char *cp, vString *const identifier)
|
||||
{
|
||||
vStringClear (identifier);
|
||||
while (isIdentifierCharacter ((int) *cp))
|
||||
{
|
||||
vStringPut (identifier, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (identifier);
|
||||
return cp;
|
||||
}
|
||||
|
||||
static void makeMemberTag (
|
||||
vString *const identifier, erlangKind kind, vString *const module)
|
||||
{
|
||||
if (ErlangKinds [kind].enabled && vStringLength (identifier) > 0)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (identifier));
|
||||
tag.kindName = ErlangKinds[kind].name;
|
||||
tag.kind = ErlangKinds[kind].letter;
|
||||
|
||||
if (module != NULL && vStringLength (module) > 0)
|
||||
{
|
||||
tag.extensionFields.scope [0] = "module";
|
||||
tag.extensionFields.scope [1] = vStringValue (module);
|
||||
}
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
}
|
||||
|
||||
static void parseModuleTag (const unsigned char *cp, vString *const module)
|
||||
{
|
||||
vString *const identifier = vStringNew ();
|
||||
parseIdentifier (cp, identifier);
|
||||
makeSimpleTag (identifier, ErlangKinds, K_MODULE);
|
||||
|
||||
/* All further entries go in the new module */
|
||||
vStringCopy (module, identifier);
|
||||
vStringDelete (identifier);
|
||||
}
|
||||
|
||||
static void parseSimpleTag (const unsigned char *cp, erlangKind kind)
|
||||
{
|
||||
vString *const identifier = vStringNew ();
|
||||
parseIdentifier (cp, identifier);
|
||||
makeSimpleTag (identifier, ErlangKinds, kind);
|
||||
vStringDelete (identifier);
|
||||
}
|
||||
|
||||
static void parseFunctionTag (const unsigned char *cp, vString *const module)
|
||||
{
|
||||
vString *const identifier = vStringNew ();
|
||||
parseIdentifier (cp, identifier);
|
||||
makeMemberTag (identifier, K_FUNCTION, module);
|
||||
vStringDelete (identifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* Directives are of the form:
|
||||
* -module(foo)
|
||||
* -define(foo, bar)
|
||||
* -record(graph, {vtab = notable, cyclic = true}).
|
||||
*/
|
||||
static void parseDirective (const unsigned char *cp, vString *const module)
|
||||
{
|
||||
/*
|
||||
* A directive will be either a record definition or a directive.
|
||||
* Record definitions are handled separately
|
||||
*/
|
||||
vString *const directive = vStringNew ();
|
||||
const char *const drtv = vStringValue (directive);
|
||||
cp = parseIdentifier (cp, directive);
|
||||
cp = skipSpace (cp);
|
||||
if (*cp == '(')
|
||||
++cp;
|
||||
|
||||
if (strcmp (drtv, "record") == 0)
|
||||
parseSimpleTag (cp, K_RECORD);
|
||||
else if (strcmp (drtv, "define") == 0)
|
||||
parseSimpleTag (cp, K_MACRO);
|
||||
else if (strcmp (drtv, "module") == 0)
|
||||
parseModuleTag (cp, module);
|
||||
/* Otherwise, it was an import, export, etc. */
|
||||
|
||||
vStringDelete (directive);
|
||||
}
|
||||
|
||||
static void findErlangTags (void)
|
||||
{
|
||||
vString *const module = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
|
||||
if (*cp == '%') /* skip initial comment */
|
||||
continue;
|
||||
if (*cp == '"') /* strings sometimes start in column one */
|
||||
continue;
|
||||
|
||||
if ( *cp == '-')
|
||||
{
|
||||
++cp; /* Move off of the '-' */
|
||||
parseDirective(cp, module);
|
||||
}
|
||||
else if (isIdentifierFirstCharacter ((int) *cp))
|
||||
parseFunctionTag (cp, module);
|
||||
}
|
||||
vStringDelete (module);
|
||||
}
|
||||
|
||||
extern parserDefinition *ErlangParser (void)
|
||||
{
|
||||
static const char *const extensions[] = { "erl", "ERL", "hrl", "HRL", NULL };
|
||||
parserDefinition *def = parserNew ("Erlang");
|
||||
def->kinds = ErlangKinds;
|
||||
def->kindCount = KIND_COUNT (ErlangKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findErlangTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* $Id: general.h,v 1.15 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Provides the general (non-ctags-specific) environment assumed by all.
|
||||
*/
|
||||
#ifndef _GENERAL_H
|
||||
#define _GENERAL_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#elif defined (AMIGA)
|
||||
# include "e_amiga.h"
|
||||
#elif defined (DJGPP)
|
||||
# include "e_djgpp.h"
|
||||
#elif defined (macintosh)
|
||||
# include "e_mac.h"
|
||||
#elif defined (MSDOS) || defined (WIN32)
|
||||
# include "e_msoft.h"
|
||||
#elif defined (OS2)
|
||||
# include "e_os2.h"
|
||||
#elif defined (QDOS)
|
||||
# include "e_qdos.h"
|
||||
#elif defined (RISCOS)
|
||||
# include "e_riscos.h"
|
||||
#elif defined (__vms) || defined (VMS)
|
||||
# include "e_vms.h"
|
||||
# ifndef VMS
|
||||
# define VMS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
|
||||
/* Define standard error destination
|
||||
*/
|
||||
#ifndef errout
|
||||
# define errout stderr
|
||||
#endif
|
||||
|
||||
/* Define regex if supported */
|
||||
#if (defined (HAVE_REGCOMP) && !defined (REGCOMP_BROKEN))
|
||||
# define HAVE_REGEX 1
|
||||
#endif
|
||||
|
||||
/* This is a helpful internal feature of later versions (> 2.7) of GCC
|
||||
* to prevent warnings about unused variables.
|
||||
*/
|
||||
#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)) && !(defined (__APPLE_CC__) || defined (__GNUG__))
|
||||
# define __unused__ __attribute__((unused))
|
||||
# define __printf__(s,f) __attribute__((format (printf, s, f)))
|
||||
#else
|
||||
# define __unused__
|
||||
# define __printf__(s,f)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Portability macros
|
||||
*/
|
||||
#if !defined(HAVE_STRCASECMP) && !defined(strcasecmp)
|
||||
# ifdef HAVE_STRICMP
|
||||
# define strcasecmp(s1,s2) stricmp(s1,s2)
|
||||
# else
|
||||
# define strcasecmp(s1,s2) struppercmp(s1,s2)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_STRNCASECMP) && !defined(strncasecmp)
|
||||
# ifdef HAVE_STRNICMP
|
||||
# define strncasecmp(s1,s2,n) strnicmp(s1,s2,n)
|
||||
# else
|
||||
# define strncasecmp(s1,s2,n) strnuppercmp(s1,s2,n)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
#undef FALSE
|
||||
#undef TRUE
|
||||
#ifdef VAXC
|
||||
typedef enum { FALSE, TRUE } booleanType;
|
||||
typedef int boolean;
|
||||
#else
|
||||
# ifdef __cplusplus
|
||||
typedef bool boolean;
|
||||
#define FALSE false
|
||||
#define TRUE true
|
||||
# else
|
||||
typedef enum { FALSE, TRUE } boolean;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ! defined (HAVE_FGETPOS) && ! defined (fpos_t)
|
||||
# define fpos_t long
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
|
||||
#if defined (NEED_PROTO_REMOVE) && defined (HAVE_REMOVE)
|
||||
extern int remove (const char *);
|
||||
#endif
|
||||
|
||||
#if defined (NEED_PROTO_UNLINK) && ! defined (HAVE_REMOVE)
|
||||
extern void *unlink (const char *);
|
||||
#endif
|
||||
|
||||
#ifdef NEED_PROTO_GETENV
|
||||
extern char *getenv (const char *);
|
||||
#endif
|
||||
|
||||
#endif /* _GENERAL_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* $Id: get.c,v 1.9 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains the high level source read functions (preprocessor
|
||||
* directives are handled within this level).
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "entry.h"
|
||||
#include "get.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define stringMatch(s1,s2) (strcmp (s1,s2) == 0)
|
||||
#define isspacetab(c) ((c) == SPACE || (c) == TAB)
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef enum { COMMENT_NONE, COMMENT_C, COMMENT_CPLUS } Comment;
|
||||
|
||||
enum eCppLimits {
|
||||
MaxCppNestingLevel = 20,
|
||||
MaxDirectiveName = 10
|
||||
};
|
||||
|
||||
/* Defines the one nesting level of a preprocessor conditional.
|
||||
*/
|
||||
typedef struct sConditionalInfo {
|
||||
boolean ignoreAllBranches; /* ignoring parent conditional branch */
|
||||
boolean singleBranch; /* choose only one branch */
|
||||
boolean branchChosen; /* branch already selected */
|
||||
boolean ignoring; /* current ignore state */
|
||||
} conditionalInfo;
|
||||
|
||||
enum eState {
|
||||
DRCTV_NONE, /* no known directive - ignore to end of line */
|
||||
DRCTV_DEFINE, /* "#define" encountered */
|
||||
DRCTV_HASH, /* initial '#' read; determine directive */
|
||||
DRCTV_IF, /* "#if" or "#ifdef" encountered */
|
||||
DRCTV_PRAGMA, /* #pragma encountered */
|
||||
DRCTV_UNDEF /* "#undef" encountered */
|
||||
};
|
||||
|
||||
/* Defines the current state of the pre-processor.
|
||||
*/
|
||||
typedef struct sCppState {
|
||||
int ungetch, ungetch2; /* ungotten characters, if any */
|
||||
boolean resolveRequired; /* must resolve if/else/elif/endif branch */
|
||||
struct sDirective {
|
||||
enum eState state; /* current directive being processed */
|
||||
boolean accept; /* is a directive syntatically permitted? */
|
||||
vString * name; /* macro name */
|
||||
unsigned int nestLevel; /* level 0 is not used */
|
||||
conditionalInfo ifdef [MaxCppNestingLevel];
|
||||
} directive;
|
||||
} cppState;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Use brace formatting to detect end of block.
|
||||
*/
|
||||
static boolean BraceFormat = FALSE;
|
||||
|
||||
static cppState Cpp = {
|
||||
'\0', '\0', /* ungetch characters */
|
||||
FALSE, /* resolveRequired */
|
||||
{
|
||||
DRCTV_NONE, /* state */
|
||||
FALSE, /* accept */
|
||||
NULL, /* tag name */
|
||||
0, /* nestLevel */
|
||||
{ {FALSE,FALSE,FALSE,FALSE} } /* ifdef array */
|
||||
} /* directive */
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern boolean isBraceFormat (void)
|
||||
{
|
||||
return BraceFormat;
|
||||
}
|
||||
|
||||
extern unsigned int getDirectiveNestLevel (void)
|
||||
{
|
||||
return Cpp.directive.nestLevel;
|
||||
}
|
||||
|
||||
extern void cppInit (const boolean state)
|
||||
{
|
||||
BraceFormat = state;
|
||||
|
||||
Cpp.ungetch = '\0';
|
||||
Cpp.ungetch2 = '\0';
|
||||
Cpp.resolveRequired = FALSE;
|
||||
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
Cpp.directive.accept = TRUE;
|
||||
Cpp.directive.nestLevel = 0;
|
||||
|
||||
Cpp.directive.ifdef [0].ignoreAllBranches = FALSE;
|
||||
Cpp.directive.ifdef [0].singleBranch = FALSE;
|
||||
Cpp.directive.ifdef [0].branchChosen = FALSE;
|
||||
Cpp.directive.ifdef [0].ignoring = FALSE;
|
||||
|
||||
if (Cpp.directive.name == NULL)
|
||||
Cpp.directive.name = vStringNew ();
|
||||
else
|
||||
vStringClear (Cpp.directive.name);
|
||||
}
|
||||
|
||||
extern void cppTerminate (void)
|
||||
{
|
||||
if (Cpp.directive.name != NULL)
|
||||
{
|
||||
vStringDelete (Cpp.directive.name);
|
||||
Cpp.directive.name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
extern void cppBeginStatement (void)
|
||||
{
|
||||
Cpp.resolveRequired = TRUE;
|
||||
}
|
||||
|
||||
extern void cppEndStatement (void)
|
||||
{
|
||||
Cpp.resolveRequired = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scanning functions
|
||||
*
|
||||
* This section handles preprocessor directives. It strips out all
|
||||
* directives and may emit a tag for #define directives.
|
||||
*/
|
||||
|
||||
/* This puts a character back into the input queue for the source File.
|
||||
* Up to two characters may be ungotten.
|
||||
*/
|
||||
extern void cppUngetc (const int c)
|
||||
{
|
||||
Assert (Cpp.ungetch2 == '\0');
|
||||
Cpp.ungetch2 = Cpp.ungetch;
|
||||
Cpp.ungetch = c;
|
||||
}
|
||||
|
||||
/* Reads a directive, whose first character is given by "c", into "name".
|
||||
*/
|
||||
static boolean readDirective (int c, char *const name, unsigned int maxLength)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0 ; i < maxLength - 1 ; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
c = fileGetc ();
|
||||
if (c == EOF || ! isalpha (c))
|
||||
{
|
||||
fileUngetc (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
name [i] = c;
|
||||
}
|
||||
name [i] = '\0'; /* null terminate */
|
||||
|
||||
return (boolean) isspacetab (c);
|
||||
}
|
||||
|
||||
/* Reads an identifier, whose first character is given by "c", into "tag",
|
||||
* together with the file location and corresponding line number.
|
||||
*/
|
||||
static void readIdentifier (int c, vString *const name)
|
||||
{
|
||||
vStringClear (name);
|
||||
do
|
||||
{
|
||||
vStringPut (name, c);
|
||||
} while (c = fileGetc (), (c != EOF && isident (c)));
|
||||
fileUngetc (c);
|
||||
vStringTerminate (name);
|
||||
}
|
||||
|
||||
static conditionalInfo *currentConditional (void)
|
||||
{
|
||||
return &Cpp.directive.ifdef [Cpp.directive.nestLevel];
|
||||
}
|
||||
|
||||
static boolean isIgnore (void)
|
||||
{
|
||||
return Cpp.directive.ifdef [Cpp.directive.nestLevel].ignoring;
|
||||
}
|
||||
|
||||
static boolean setIgnore (const boolean ignore)
|
||||
{
|
||||
return Cpp.directive.ifdef [Cpp.directive.nestLevel].ignoring = ignore;
|
||||
}
|
||||
|
||||
static boolean isIgnoreBranch (void)
|
||||
{
|
||||
conditionalInfo *const ifdef = currentConditional ();
|
||||
|
||||
/* Force a single branch if an incomplete statement is discovered
|
||||
* en route. This may have allowed earlier branches containing complete
|
||||
* statements to be followed, but we must follow no further branches.
|
||||
*/
|
||||
if (Cpp.resolveRequired && ! BraceFormat)
|
||||
ifdef->singleBranch = TRUE;
|
||||
|
||||
/* We will ignore this branch in the following cases:
|
||||
*
|
||||
* 1. We are ignoring all branches (conditional was within an ignored
|
||||
* branch of the parent conditional)
|
||||
* 2. A branch has already been chosen and either of:
|
||||
* a. A statement was incomplete upon entering the conditional
|
||||
* b. A statement is incomplete upon encountering a branch
|
||||
*/
|
||||
return (boolean) (ifdef->ignoreAllBranches ||
|
||||
(ifdef->branchChosen && ifdef->singleBranch));
|
||||
}
|
||||
|
||||
static void chooseBranch (void)
|
||||
{
|
||||
if (! BraceFormat)
|
||||
{
|
||||
conditionalInfo *const ifdef = currentConditional ();
|
||||
|
||||
ifdef->branchChosen = (boolean) (ifdef->singleBranch ||
|
||||
Cpp.resolveRequired);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pushes one nesting level for an #if directive, indicating whether or not
|
||||
* the branch should be ignored and whether a branch has already been chosen.
|
||||
*/
|
||||
static boolean pushConditional (const boolean firstBranchChosen)
|
||||
{
|
||||
const boolean ignoreAllBranches = isIgnore (); /* current ignore */
|
||||
boolean ignoreBranch = FALSE;
|
||||
|
||||
if (Cpp.directive.nestLevel < (unsigned int) MaxCppNestingLevel - 1)
|
||||
{
|
||||
conditionalInfo *ifdef;
|
||||
|
||||
++Cpp.directive.nestLevel;
|
||||
ifdef = currentConditional ();
|
||||
|
||||
/* We take a snapshot of whether there is an incomplete statement in
|
||||
* progress upon encountering the preprocessor conditional. If so,
|
||||
* then we will flag that only a single branch of the conditional
|
||||
* should be followed.
|
||||
*/
|
||||
ifdef->ignoreAllBranches = ignoreAllBranches;
|
||||
ifdef->singleBranch = Cpp.resolveRequired;
|
||||
ifdef->branchChosen = firstBranchChosen;
|
||||
ifdef->ignoring = (boolean) (ignoreAllBranches || (
|
||||
! firstBranchChosen && ! BraceFormat &&
|
||||
(ifdef->singleBranch || !Option.if0)));
|
||||
ignoreBranch = ifdef->ignoring;
|
||||
}
|
||||
return ignoreBranch;
|
||||
}
|
||||
|
||||
/* Pops one nesting level for an #endif directive.
|
||||
*/
|
||||
static boolean popConditional (void)
|
||||
{
|
||||
if (Cpp.directive.nestLevel > 0)
|
||||
--Cpp.directive.nestLevel;
|
||||
|
||||
return isIgnore ();
|
||||
}
|
||||
|
||||
static void makeDefineTag (const char *const name)
|
||||
{
|
||||
const boolean isFileScope = (boolean) (! isHeaderFile ());
|
||||
|
||||
if (includingDefineTags () &&
|
||||
(! isFileScope || Option.include.fileScope))
|
||||
{
|
||||
tagEntryInfo e;
|
||||
initTagEntry (&e, name);
|
||||
e.lineNumberEntry = (boolean) (Option.locate != EX_PATTERN);
|
||||
e.isFileScope = isFileScope;
|
||||
e.truncateLine = TRUE;
|
||||
e.kindName = "macro";
|
||||
e.kind = 'd';
|
||||
makeTagEntry (&e);
|
||||
}
|
||||
}
|
||||
|
||||
static void directiveDefine (const int c)
|
||||
{
|
||||
if (isident1 (c))
|
||||
{
|
||||
readIdentifier (c, Cpp.directive.name);
|
||||
if (! isIgnore ())
|
||||
makeDefineTag (vStringValue (Cpp.directive.name));
|
||||
}
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
}
|
||||
|
||||
static void directivePragma (int c)
|
||||
{
|
||||
if (isident1 (c))
|
||||
{
|
||||
readIdentifier (c, Cpp.directive.name);
|
||||
if (stringMatch (vStringValue (Cpp.directive.name), "weak"))
|
||||
{
|
||||
/* generate macro tag for weak name */
|
||||
do
|
||||
{
|
||||
c = fileGetc ();
|
||||
} while (c == SPACE);
|
||||
if (isident1 (c))
|
||||
{
|
||||
readIdentifier (c, Cpp.directive.name);
|
||||
makeDefineTag (vStringValue (Cpp.directive.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
}
|
||||
|
||||
static boolean directiveIf (const int c)
|
||||
{
|
||||
DebugStatement ( const boolean ignore0 = isIgnore (); )
|
||||
const boolean ignore = pushConditional ((boolean) (c != '0'));
|
||||
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
DebugStatement ( debugCppNest (TRUE, Cpp.directive.nestLevel);
|
||||
if (ignore != ignore0) debugCppIgnore (ignore); )
|
||||
|
||||
return ignore;
|
||||
}
|
||||
|
||||
static boolean directiveHash (const int c)
|
||||
{
|
||||
boolean ignore = FALSE;
|
||||
char directive [MaxDirectiveName];
|
||||
DebugStatement ( const boolean ignore0 = isIgnore (); )
|
||||
|
||||
readDirective (c, directive, MaxDirectiveName);
|
||||
if (stringMatch (directive, "define"))
|
||||
Cpp.directive.state = DRCTV_DEFINE;
|
||||
else if (stringMatch (directive, "undef"))
|
||||
Cpp.directive.state = DRCTV_UNDEF;
|
||||
else if (strncmp (directive, "if", (size_t) 2) == 0)
|
||||
Cpp.directive.state = DRCTV_IF;
|
||||
else if (stringMatch (directive, "elif") ||
|
||||
stringMatch (directive, "else"))
|
||||
{
|
||||
ignore = setIgnore (isIgnoreBranch ());
|
||||
if (! ignore && stringMatch (directive, "else"))
|
||||
chooseBranch ();
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
DebugStatement ( if (ignore != ignore0) debugCppIgnore (ignore); )
|
||||
}
|
||||
else if (stringMatch (directive, "endif"))
|
||||
{
|
||||
DebugStatement ( debugCppNest (FALSE, Cpp.directive.nestLevel); )
|
||||
ignore = popConditional ();
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
DebugStatement ( if (ignore != ignore0) debugCppIgnore (ignore); )
|
||||
}
|
||||
else if (stringMatch (directive, "pragma"))
|
||||
Cpp.directive.state = DRCTV_PRAGMA;
|
||||
else
|
||||
Cpp.directive.state = DRCTV_NONE;
|
||||
|
||||
return ignore;
|
||||
}
|
||||
|
||||
/* Handles a pre-processor directive whose first character is given by "c".
|
||||
*/
|
||||
static boolean handleDirective (const int c)
|
||||
{
|
||||
boolean ignore = isIgnore ();
|
||||
|
||||
switch (Cpp.directive.state)
|
||||
{
|
||||
case DRCTV_NONE: ignore = isIgnore (); break;
|
||||
case DRCTV_DEFINE: directiveDefine (c); break;
|
||||
case DRCTV_HASH: ignore = directiveHash (c); break;
|
||||
case DRCTV_IF: ignore = directiveIf (c); break;
|
||||
case DRCTV_PRAGMA: directivePragma (c); break;
|
||||
case DRCTV_UNDEF: directiveDefine (c); break;
|
||||
}
|
||||
return ignore;
|
||||
}
|
||||
|
||||
/* Called upon reading of a slash ('/') characters, determines whether a
|
||||
* comment is encountered, and its type.
|
||||
*/
|
||||
static Comment isComment (void)
|
||||
{
|
||||
Comment comment;
|
||||
const int next = fileGetc ();
|
||||
|
||||
if (next == '*')
|
||||
comment = COMMENT_C;
|
||||
else if (next == '/')
|
||||
comment = COMMENT_CPLUS;
|
||||
else
|
||||
{
|
||||
fileUngetc (next);
|
||||
comment = COMMENT_NONE;
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
/* Skips over a C style comment. According to ANSI specification a comment
|
||||
* is treated as white space, so we perform this subsitution.
|
||||
*/
|
||||
static int skipOverCComment (void)
|
||||
{
|
||||
int c = fileGetc ();
|
||||
|
||||
while (c != EOF)
|
||||
{
|
||||
if (c != '*')
|
||||
c = fileGetc ();
|
||||
else
|
||||
{
|
||||
const int next = fileGetc ();
|
||||
|
||||
if (next != '/')
|
||||
c = next;
|
||||
else
|
||||
{
|
||||
c = SPACE; /* replace comment with space */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Skips over a C++ style comment.
|
||||
*/
|
||||
static int skipOverCplusComment (void)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = fileGetc ()) != EOF)
|
||||
{
|
||||
if (c == BACKSLASH)
|
||||
fileGetc (); /* throw away next character, too */
|
||||
else if (c == NEWLINE)
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Skips to the end of a string, returning a special character to
|
||||
* symbolically represent a generic string.
|
||||
*/
|
||||
static int skipToEndOfString (void)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = fileGetc ()) != EOF)
|
||||
{
|
||||
if (c == BACKSLASH)
|
||||
fileGetc (); /* throw away next character, too */
|
||||
else if (c == DOUBLE_QUOTE)
|
||||
break;
|
||||
}
|
||||
return STRING_SYMBOL; /* symbolic representation of string */
|
||||
}
|
||||
|
||||
/* Skips to the end of the three (possibly four) 'c' sequence, returning a
|
||||
* special character to symbolically represent a generic character.
|
||||
* Also detects Vera numbers that include a base specifier (ie. 'b1010).
|
||||
*/
|
||||
static int skipToEndOfChar (void)
|
||||
{
|
||||
int c;
|
||||
int count = 0, veraBase = '\0';
|
||||
|
||||
while ((c = fileGetc ()) != EOF)
|
||||
{
|
||||
++count;
|
||||
if (c == BACKSLASH)
|
||||
fileGetc (); /* throw away next character, too */
|
||||
else if (c == SINGLE_QUOTE)
|
||||
break;
|
||||
else if (c == NEWLINE)
|
||||
{
|
||||
fileUngetc (c);
|
||||
break;
|
||||
}
|
||||
else if (count == 1 && strchr ("DHOB", toupper (c)) != NULL)
|
||||
veraBase = c;
|
||||
else if (veraBase != '\0' && ! isalnum (c))
|
||||
{
|
||||
fileUngetc (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CHAR_SYMBOL; /* symbolic representation of character */
|
||||
}
|
||||
|
||||
/* This function returns the next character, stripping out comments,
|
||||
* C pre-processor directives, and the contents of single and double
|
||||
* quoted strings. In short, strip anything which places a burden upon
|
||||
* the tokenizer.
|
||||
*/
|
||||
extern int cppGetc (void)
|
||||
{
|
||||
boolean directive = FALSE;
|
||||
boolean ignore = FALSE;
|
||||
int c;
|
||||
|
||||
if (Cpp.ungetch != '\0')
|
||||
{
|
||||
c = Cpp.ungetch;
|
||||
Cpp.ungetch = Cpp.ungetch2;
|
||||
Cpp.ungetch2 = '\0';
|
||||
return c; /* return here to avoid re-calling debugPutc () */
|
||||
}
|
||||
else do
|
||||
{
|
||||
c = fileGetc ();
|
||||
process:
|
||||
switch (c)
|
||||
{
|
||||
case EOF:
|
||||
ignore = FALSE;
|
||||
directive = FALSE;
|
||||
break;
|
||||
|
||||
case TAB:
|
||||
case SPACE:
|
||||
break; /* ignore most white space */
|
||||
|
||||
case NEWLINE:
|
||||
if (directive && ! ignore)
|
||||
directive = FALSE;
|
||||
Cpp.directive.accept = TRUE;
|
||||
break;
|
||||
|
||||
case DOUBLE_QUOTE:
|
||||
Cpp.directive.accept = FALSE;
|
||||
c = skipToEndOfString ();
|
||||
break;
|
||||
|
||||
case '#':
|
||||
if (Cpp.directive.accept)
|
||||
{
|
||||
directive = TRUE;
|
||||
Cpp.directive.state = DRCTV_HASH;
|
||||
Cpp.directive.accept = FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case SINGLE_QUOTE:
|
||||
Cpp.directive.accept = FALSE;
|
||||
c = skipToEndOfChar ();
|
||||
break;
|
||||
|
||||
case '/':
|
||||
{
|
||||
const Comment comment = isComment ();
|
||||
|
||||
if (comment == COMMENT_C)
|
||||
c = skipOverCComment ();
|
||||
else if (comment == COMMENT_CPLUS)
|
||||
{
|
||||
c = skipOverCplusComment ();
|
||||
if (c == NEWLINE)
|
||||
fileUngetc (c);
|
||||
}
|
||||
else
|
||||
Cpp.directive.accept = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
case BACKSLASH:
|
||||
{
|
||||
int next = fileGetc ();
|
||||
|
||||
if (next == NEWLINE)
|
||||
continue;
|
||||
else if (next == '?')
|
||||
cppUngetc (next);
|
||||
else
|
||||
fileUngetc (next);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
{
|
||||
int next = fileGetc ();
|
||||
if (next != '?')
|
||||
fileUngetc (next);
|
||||
else
|
||||
{
|
||||
next = fileGetc ();
|
||||
switch (next)
|
||||
{
|
||||
case '(': c = '['; break;
|
||||
case ')': c = ']'; break;
|
||||
case '<': c = '{'; break;
|
||||
case '>': c = '}'; break;
|
||||
case '/': c = BACKSLASH; goto process;
|
||||
case '!': c = '|'; break;
|
||||
case SINGLE_QUOTE: c = '^'; break;
|
||||
case '-': c = '~'; break;
|
||||
case '=': c = '#'; goto process;
|
||||
default:
|
||||
fileUngetc (next);
|
||||
cppUngetc ('?');
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
Cpp.directive.accept = FALSE;
|
||||
if (directive)
|
||||
ignore = handleDirective (c);
|
||||
break;
|
||||
}
|
||||
} while (directive || ignore);
|
||||
|
||||
DebugStatement ( debugPutc (DEBUG_CPP, c); )
|
||||
DebugStatement ( if (c == NEWLINE)
|
||||
debugPrintf (DEBUG_CPP, "%6ld: ", getInputLineNumber () + 1); )
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* $Id: get.h,v 1.4 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to get.c
|
||||
*/
|
||||
#ifndef _GET_H
|
||||
#define _GET_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include "ctags.h" /* to define langType */
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
/* Is the character valid as a character of a C identifier?
|
||||
* VMS allows '$' in identifiers.
|
||||
*/
|
||||
#define isident(c) (isalnum(c) || (c) == '_' || (c) == '$')
|
||||
|
||||
/* Is the character valid as the first character of a C identifier?
|
||||
* C++ allows '~' in destructors.
|
||||
* VMS allows '$' in identifiers.
|
||||
*/
|
||||
#define isident1(c) (isalpha(c) || (c) == '_' || (c) == '~' || (c) == '$')
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern boolean isBraceFormat (void);
|
||||
extern unsigned int getDirectiveNestLevel (void);
|
||||
extern void cppInit (const boolean state);
|
||||
extern void cppTerminate (void);
|
||||
extern void cppBeginStatement (void);
|
||||
extern void cppEndStatement (void);
|
||||
extern void cppUngetc (const int c);
|
||||
extern int cppGetc (void);
|
||||
|
||||
#endif /* _GET_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* $Id: html.c,v 1.2 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for HTML language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installHtmlRegex (const langType language)
|
||||
{
|
||||
#define POSSIBLE_ATTRIBUTES "([ \t]+[a-z]+=\"?[^>\"]*\"?)*"
|
||||
addTagRegex (language,
|
||||
"<a"
|
||||
POSSIBLE_ATTRIBUTES
|
||||
"[ \t]+name=\"?([^>\"]+)\"?"
|
||||
POSSIBLE_ATTRIBUTES
|
||||
"[ \t]*>",
|
||||
"\\2", "a,anchor,named anchors", "i");
|
||||
|
||||
addTagRegex (language, "^[ \t]*function[ \t]*([A-Za-z0-9_]+)[ \t]*\\(",
|
||||
"\\1", "f,function,JavaScript functions", NULL);
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* HtmlParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "htm", "html", NULL };
|
||||
parserDefinition *const def = parserNew ("HTML");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installHtmlRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* $Id: jscript.c,v 1.2 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for JavaScript language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installJavaScriptRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language, "^[ \t]*function[ \t]*([A-Za-z0-9_]+)[ \t]*\\(",
|
||||
"\\1", "f,function,functions", NULL);
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* JavaScriptParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "js", NULL };
|
||||
parserDefinition *const def = parserNew ("JavaScript");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installJavaScriptRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* $Id: keyword.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Manages a keyword hash.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "keyword.h"
|
||||
#include "options.h"
|
||||
#include "routines.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define HASH_EXPONENT 7 /* must be less than 17 */
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef struct sHashEntry {
|
||||
struct sHashEntry *next;
|
||||
const char *string;
|
||||
langType language;
|
||||
int value;
|
||||
} hashEntry;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static const unsigned int TableSize = 1 << HASH_EXPONENT;
|
||||
static hashEntry **HashTable = NULL;
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static hashEntry **getHashTable (void)
|
||||
{
|
||||
static boolean allocated = FALSE;
|
||||
|
||||
if (! allocated)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
HashTable = xMalloc (TableSize, hashEntry*);
|
||||
|
||||
for (i = 0 ; i < TableSize ; ++i)
|
||||
HashTable [i] = NULL;
|
||||
|
||||
allocated = TRUE;
|
||||
}
|
||||
return HashTable;
|
||||
}
|
||||
|
||||
static hashEntry *getHashTableEntry (unsigned long hashedValue)
|
||||
{
|
||||
hashEntry **const table = getHashTable ();
|
||||
hashEntry *entry;
|
||||
|
||||
Assert (hashedValue < TableSize);
|
||||
entry = table [hashedValue];
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static unsigned long hashValue (const char *const string)
|
||||
{
|
||||
unsigned long value = 0;
|
||||
const unsigned char *p;
|
||||
|
||||
Assert (string != NULL);
|
||||
|
||||
/* We combine the various words of the multiword key using the method
|
||||
* described on page 512 of Vol. 3 of "The Art of Computer Programming".
|
||||
*/
|
||||
for (p = (const unsigned char *) string ; *p != '\0' ; ++p)
|
||||
{
|
||||
value <<= 1;
|
||||
if (value & 0x00000100L)
|
||||
value = (value & 0x000000ffL) + 1L;
|
||||
value ^= *p;
|
||||
}
|
||||
/* Algorithm from page 509 of Vol. 3 of "The Art of Computer Programming"
|
||||
* Treats "value" as a 16-bit integer plus 16-bit fraction.
|
||||
*/
|
||||
value *= 40503L; /* = 2^16 * 0.6180339887 ("golden ratio") */
|
||||
value &= 0x0000ffffL; /* keep fractional part */
|
||||
value >>= 16 - HASH_EXPONENT; /* scale up by hash size and move down */
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static hashEntry *newEntry (
|
||||
const char *const string, langType language, int value)
|
||||
{
|
||||
hashEntry *const entry = xMalloc (1, hashEntry);
|
||||
|
||||
entry->next = NULL;
|
||||
entry->string = string;
|
||||
entry->language = language;
|
||||
entry->value = value;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Note that it is assumed that a "value" of zero means an undefined keyword
|
||||
* and clients of this function should observe this. Also, all keywords added
|
||||
* should be added in lower case. If we encounter a case-sensitive language
|
||||
* whose keywords are in upper case, we will need to redesign this.
|
||||
*/
|
||||
extern void addKeyword (const char *const string, langType language, int value)
|
||||
{
|
||||
const unsigned long hashedValue = hashValue (string);
|
||||
hashEntry *tableEntry = getHashTableEntry (hashedValue);
|
||||
hashEntry *entry = tableEntry;
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
hashEntry **const table = getHashTable ();
|
||||
table [hashedValue] = newEntry (string, language, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
hashEntry *prev = NULL;
|
||||
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (language == entry->language &&
|
||||
strcmp (string, entry->string) == 0)
|
||||
{
|
||||
Assert (("Already in table" == NULL));
|
||||
}
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
if (entry == NULL)
|
||||
{
|
||||
Assert (prev != NULL);
|
||||
prev->next = newEntry (string, language, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int lookupKeyword (const char *const string, langType language)
|
||||
{
|
||||
const unsigned long hashedValue = hashValue (string);
|
||||
hashEntry *entry = getHashTableEntry (hashedValue);
|
||||
int result = -1;
|
||||
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (language == entry->language && strcmp (string, entry->string) == 0)
|
||||
{
|
||||
result = entry->value;
|
||||
break;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void freeKeywordTable (void)
|
||||
{
|
||||
if (HashTable != NULL)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0 ; i < TableSize ; ++i)
|
||||
{
|
||||
hashEntry *entry = HashTable [i];
|
||||
|
||||
while (entry != NULL)
|
||||
{
|
||||
hashEntry *next = entry->next;
|
||||
eFree (entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
eFree (HashTable);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
static void printEntry (const hashEntry *const entry)
|
||||
{
|
||||
printf (" %-15s %-7s\n", entry->string, getLanguageName (entry->language));
|
||||
}
|
||||
|
||||
static unsigned int printBucket (const unsigned int i)
|
||||
{
|
||||
hashEntry **const table = getHashTable ();
|
||||
hashEntry *entry = table [i];
|
||||
unsigned int measure = 1;
|
||||
boolean first = TRUE;
|
||||
|
||||
printf ("%2d:", i);
|
||||
if (entry == NULL)
|
||||
printf ("\n");
|
||||
else while (entry != NULL)
|
||||
{
|
||||
if (! first)
|
||||
printf (" ");
|
||||
else
|
||||
{
|
||||
printf (" ");
|
||||
first = FALSE;
|
||||
}
|
||||
printEntry (entry);
|
||||
entry = entry->next;
|
||||
measure = 2 * measure;
|
||||
}
|
||||
return measure - 1;
|
||||
}
|
||||
|
||||
extern void printKeywordTable (void)
|
||||
{
|
||||
unsigned long emptyBucketCount = 0;
|
||||
unsigned long measure = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0 ; i < TableSize ; ++i)
|
||||
{
|
||||
const unsigned int pass = printBucket (i);
|
||||
|
||||
measure += pass;
|
||||
if (pass == 0)
|
||||
++emptyBucketCount;
|
||||
}
|
||||
|
||||
printf ("spread measure = %ld\n", measure);
|
||||
printf ("%ld empty buckets\n", emptyBucketCount);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* $Id: keyword.h,v 1.3 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to keyword.c
|
||||
*/
|
||||
#ifndef _KEYWORD_H
|
||||
#define _KEYWORD_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void addKeyword (const char *const string, langType language, int value);
|
||||
extern int lookupKeyword (const char *const string, langType language);
|
||||
extern void freeKeywordTable (void);
|
||||
#ifdef DEBUG
|
||||
extern void printKeywordTable (void);
|
||||
#endif
|
||||
|
||||
#endif /* _KEYWORD_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* $Id: lisp.c,v 1.3 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for LISP files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_FUNCTION
|
||||
} lispKind;
|
||||
|
||||
static kindOption LispKinds [] = {
|
||||
{ TRUE, 'f', "function", "functions" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/*
|
||||
* lisp tag functions
|
||||
* look for (def or (DEF, quote or QUOTE
|
||||
*/
|
||||
static int L_isdef (const unsigned char *strp)
|
||||
{
|
||||
return ( (strp [1] == 'd' || strp [1] == 'D')
|
||||
&& (strp [2] == 'e' || strp [2] == 'E')
|
||||
&& (strp [3] == 'f' || strp [3] == 'F'));
|
||||
}
|
||||
|
||||
static int L_isquote (const unsigned char *strp)
|
||||
{
|
||||
return ( (*(++strp) == 'q' || *strp == 'Q')
|
||||
&& (*(++strp) == 'u' || *strp == 'U')
|
||||
&& (*(++strp) == 'o' || *strp == 'O')
|
||||
&& (*(++strp) == 't' || *strp == 'T')
|
||||
&& (*(++strp) == 'e' || *strp == 'E')
|
||||
&& isspace (*(++strp)));
|
||||
}
|
||||
|
||||
static void L_getit (vString *const name, const unsigned char *dbp)
|
||||
{
|
||||
const unsigned char *p;
|
||||
|
||||
if (*dbp == '\'') /* Skip prefix quote */
|
||||
dbp++;
|
||||
else if (*dbp == '(' && L_isquote (dbp)) /* Skip "(quote " */
|
||||
{
|
||||
dbp += 7;
|
||||
while (isspace (*dbp))
|
||||
dbp++;
|
||||
}
|
||||
for (p=dbp ; *p!='\0' && *p!='(' && !isspace ((int) *p) && *p!=')' ; p++)
|
||||
vStringPut (name, *p);
|
||||
vStringTerminate (name);
|
||||
|
||||
if (vStringLength (name) > 0)
|
||||
makeSimpleTag (name, LispKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
|
||||
/* Algorithm adapted from from GNU etags.
|
||||
*/
|
||||
static void findLispTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char* p;
|
||||
|
||||
|
||||
while ((p = fileReadLine ()) != NULL)
|
||||
{
|
||||
if (*p == '(')
|
||||
{
|
||||
if (L_isdef (p))
|
||||
{
|
||||
while (*p != '\0' && !isspace ((int) *p))
|
||||
p++;
|
||||
while (isspace ((int) *p))
|
||||
p++;
|
||||
L_getit (name, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check for (foo::defmumble name-defined ... */
|
||||
do
|
||||
p++;
|
||||
while (*p != '\0' && !isspace ((int) *p)
|
||||
&& *p != ':' && *p != '(' && *p != ')');
|
||||
if (*p == ':')
|
||||
{
|
||||
do
|
||||
p++;
|
||||
while (*p == ':');
|
||||
|
||||
if (L_isdef (p - 1))
|
||||
{
|
||||
while (*p != '\0' && !isspace ((int) *p))
|
||||
p++;
|
||||
while (isspace (*p))
|
||||
p++;
|
||||
L_getit (name, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* LispParser (void)
|
||||
{
|
||||
static const char *const extensions [] = {
|
||||
"cl", "clisp", "el", "l", "lisp", "lsp", "ml", NULL
|
||||
};
|
||||
parserDefinition* def = parserNew ("Lisp");
|
||||
def->kinds = LispKinds;
|
||||
def->kindCount = KIND_COUNT (LispKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findLispTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,696 @@
|
||||
/*
|
||||
* $Id: lregex.c,v 1.7 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for applying regular expression matching.
|
||||
*
|
||||
* The code for utlizing the Gnu regex package with regards to processing the
|
||||
* regex option and checking for regex matches was adapted from routines in
|
||||
* Gnu etags.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_REGCOMP
|
||||
# include <ctype.h>
|
||||
# include <stddef.h>
|
||||
# ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h> /* declare off_t (not known to regex.h on FreeBSD) */
|
||||
# endif
|
||||
# include "regex.h"
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "entry.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
|
||||
#ifdef HAVE_REGEX
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
|
||||
/* Back-references \0 through \9 */
|
||||
#define BACK_REFERENCE_COUNT 10
|
||||
|
||||
#if defined (HAVE_REGCOMP) && !defined (REGCOMP_BROKEN)
|
||||
# define POSIX_REGEX
|
||||
#endif
|
||||
|
||||
#define REGEX_NAME "Regex"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
#if defined (POSIX_REGEX)
|
||||
|
||||
struct sKind {
|
||||
boolean enabled;
|
||||
char letter;
|
||||
char* name;
|
||||
char* description;
|
||||
};
|
||||
|
||||
enum pType { PTRN_TAG, PTRN_CALLBACK };
|
||||
|
||||
typedef struct {
|
||||
regex_t *pattern;
|
||||
enum pType type;
|
||||
union {
|
||||
struct {
|
||||
char *name_pattern;
|
||||
struct sKind kind;
|
||||
} tag;
|
||||
struct {
|
||||
regexCallback function;
|
||||
} callback;
|
||||
} u;
|
||||
} regexPattern;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
regexPattern *patterns;
|
||||
unsigned int count;
|
||||
} patternSet;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
|
||||
static boolean regexBroken = FALSE;
|
||||
|
||||
/* Array of pattern sets, indexed by language */
|
||||
static patternSet* Sets = NULL;
|
||||
static int SetUpper = -1; /* upper language index in list */
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void clearPatternSet (const langType language)
|
||||
{
|
||||
if (language < SetUpper)
|
||||
{
|
||||
patternSet* const set = Sets + language;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < set->count ; ++i)
|
||||
{
|
||||
#if defined (POSIX_REGEX)
|
||||
regfree (set->patterns [i].pattern);
|
||||
#endif
|
||||
eFree (set->patterns [i].pattern);
|
||||
set->patterns [i].pattern = NULL;
|
||||
|
||||
if (set->patterns [i].type == PTRN_TAG)
|
||||
{
|
||||
eFree (set->patterns [i].u.tag.name_pattern);
|
||||
set->patterns [i].u.tag.name_pattern = NULL;
|
||||
}
|
||||
}
|
||||
if (set->patterns != NULL)
|
||||
eFree (set->patterns);
|
||||
set->patterns = NULL;
|
||||
set->count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Regex psuedo-parser
|
||||
*/
|
||||
|
||||
static void makeRegexTag (
|
||||
const vString* const name, const struct sKind* const kind)
|
||||
{
|
||||
if (kind->enabled)
|
||||
{
|
||||
tagEntryInfo e;
|
||||
Assert (name != NULL && vStringLength (name) > 0);
|
||||
Assert (kind != NULL);
|
||||
initTagEntry (&e, vStringValue (name));
|
||||
e.kind = kind->letter;
|
||||
e.kindName = kind->name;
|
||||
makeTagEntry (&e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Regex pattern definition
|
||||
*/
|
||||
|
||||
/* Take a string like "/blah/" and turn it into "blah", making sure
|
||||
* that the first and last characters are the same, and handling
|
||||
* quoted separator characters. Actually, stops on the occurrence of
|
||||
* an unquoted separator. Also turns "\t" into a Tab character.
|
||||
* Returns pointer to terminating separator. Works in place. Null
|
||||
* terminates name string.
|
||||
*/
|
||||
static char* scanSeparators (char* name)
|
||||
{
|
||||
char sep = name [0];
|
||||
char *copyto = name;
|
||||
boolean quoted = FALSE;
|
||||
|
||||
for (++name ; *name != '\0' ; ++name)
|
||||
{
|
||||
if (quoted)
|
||||
{
|
||||
if (*name == sep)
|
||||
*copyto++ = sep;
|
||||
else if (*name == 't')
|
||||
*copyto++ = '\t';
|
||||
else
|
||||
{
|
||||
/* Something else is quoted, so preserve the quote. */
|
||||
*copyto++ = '\\';
|
||||
*copyto++ = *name;
|
||||
}
|
||||
quoted = FALSE;
|
||||
}
|
||||
else if (*name == '\\')
|
||||
quoted = TRUE;
|
||||
else if (*name == sep)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
*copyto++ = *name;
|
||||
}
|
||||
*copyto = '\0';
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Parse `regexp', in form "/regex/name/[k,Kind/]flags" (where the separator
|
||||
* character is whatever the first character of `regexp' is), by breaking it
|
||||
* up into null terminated strings, removing the separators, and expanding
|
||||
* '\t' into tabs. When complete, `regexp' points to the line matching
|
||||
* pattern, a pointer to the name matching pattern is written to `name', a
|
||||
* pointer to the kinds is written to `kinds' (possibly NULL), and a pointer
|
||||
* to the trailing flags is written to `flags'. If the pattern is not in the
|
||||
* correct format, a false value is returned.
|
||||
*/
|
||||
static boolean parseTagRegex (
|
||||
char* const regexp, char** const name,
|
||||
char** const kinds, char** const flags)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
const int separator = (unsigned char) regexp [0];
|
||||
|
||||
*name = scanSeparators (regexp);
|
||||
if (*regexp == '\0')
|
||||
error (WARNING, "empty regexp");
|
||||
else if (**name != separator)
|
||||
error (WARNING, "%s: incomplete regexp", regexp);
|
||||
else
|
||||
{
|
||||
char* const third = scanSeparators (*name);
|
||||
if (**name == '\0')
|
||||
error (WARNING, "%s: regexp missing name pattern", regexp);
|
||||
if ((*name) [strlen (*name) - 1] == '\\')
|
||||
error (WARNING, "error in name pattern: \"%s\"", *name);
|
||||
if (*third != separator)
|
||||
error (WARNING, "%s: regexp missing final separator", regexp);
|
||||
else
|
||||
{
|
||||
char* const fourth = scanSeparators (third);
|
||||
if (*fourth == separator)
|
||||
{
|
||||
*kinds = third;
|
||||
scanSeparators (fourth);
|
||||
*flags = fourth;
|
||||
}
|
||||
else
|
||||
{
|
||||
*flags = third;
|
||||
*kinds = NULL;
|
||||
}
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void addCompiledTagPattern (
|
||||
const langType language, regex_t* const pattern,
|
||||
char* const name, const char kind, char* const kindName,
|
||||
char *const description)
|
||||
{
|
||||
patternSet* set;
|
||||
regexPattern *ptrn;
|
||||
if (language > SetUpper)
|
||||
{
|
||||
int i;
|
||||
Sets = xRealloc (Sets, (language + 1), patternSet);
|
||||
for (i = SetUpper + 1 ; i <= language ; ++i)
|
||||
{
|
||||
Sets [i].patterns = NULL;
|
||||
Sets [i].count = 0;
|
||||
}
|
||||
SetUpper = language;
|
||||
}
|
||||
set = Sets + language;
|
||||
set->patterns = xRealloc (set->patterns, (set->count + 1), regexPattern);
|
||||
ptrn = &set->patterns [set->count];
|
||||
set->count += 1;
|
||||
|
||||
ptrn->pattern = pattern;
|
||||
ptrn->type = PTRN_TAG;
|
||||
ptrn->u.tag.name_pattern = name;
|
||||
ptrn->u.tag.kind.enabled = TRUE;
|
||||
ptrn->u.tag.kind.letter = kind;
|
||||
ptrn->u.tag.kind.name = kindName;
|
||||
ptrn->u.tag.kind.description = description;
|
||||
}
|
||||
|
||||
static void addCompiledCallbackPattern (
|
||||
const langType language, regex_t* const pattern,
|
||||
const regexCallback callback)
|
||||
{
|
||||
patternSet* set;
|
||||
regexPattern *ptrn;
|
||||
if (language > SetUpper)
|
||||
{
|
||||
int i;
|
||||
Sets = xRealloc (Sets, (language + 1), patternSet);
|
||||
for (i = SetUpper + 1 ; i <= language ; ++i)
|
||||
{
|
||||
Sets [i].patterns = NULL;
|
||||
Sets [i].count = 0;
|
||||
}
|
||||
SetUpper = language;
|
||||
}
|
||||
set = Sets + language;
|
||||
set->patterns = xRealloc (set->patterns, (set->count + 1), regexPattern);
|
||||
ptrn = &set->patterns [set->count];
|
||||
set->count += 1;
|
||||
|
||||
ptrn->pattern = pattern;
|
||||
ptrn->type = PTRN_CALLBACK;
|
||||
ptrn->u.callback.function = callback;
|
||||
}
|
||||
|
||||
#if defined (POSIX_REGEX)
|
||||
|
||||
static regex_t* compileRegex (const char* const regexp, const char* const flags)
|
||||
{
|
||||
int cflags = REG_EXTENDED | REG_NEWLINE;
|
||||
regex_t *result = NULL;
|
||||
int errcode;
|
||||
int i;
|
||||
for (i = 0 ; flags != NULL && flags [i] != '\0' ; ++i)
|
||||
{
|
||||
switch ((int) flags [i])
|
||||
{
|
||||
case 'b': cflags &= ~REG_EXTENDED; break;
|
||||
case 'e': cflags |= REG_EXTENDED; break;
|
||||
case 'i': cflags |= REG_ICASE; break;
|
||||
default: error (WARNING, "unknown regex flag: '%c'", *flags); break;
|
||||
}
|
||||
}
|
||||
result = xMalloc (1, regex_t);
|
||||
errcode = regcomp (result, regexp, cflags);
|
||||
if (errcode != 0)
|
||||
{
|
||||
char errmsg[256];
|
||||
regerror (errcode, result, errmsg, 256);
|
||||
error (WARNING, "%s", errmsg);
|
||||
regfree (result);
|
||||
eFree (result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void parseKinds (
|
||||
const char* const kinds, char* const kind, char** const kindName,
|
||||
char **description)
|
||||
{
|
||||
*kind = '\0';
|
||||
*kindName = NULL;
|
||||
*description = NULL;
|
||||
if (kinds == NULL || kinds [0] == '\0')
|
||||
{
|
||||
*kind = 'r';
|
||||
*kindName = eStrdup ("regex");
|
||||
}
|
||||
else if (kinds [0] != '\0')
|
||||
{
|
||||
const char* k = kinds;
|
||||
if (k [0] != ',' && (k [1] == ',' || k [1] == '\0'))
|
||||
*kind = *k++;
|
||||
else
|
||||
*kind = 'r';
|
||||
if (*k == ',')
|
||||
++k;
|
||||
if (k [0] == '\0')
|
||||
*kindName = eStrdup ("regex");
|
||||
else
|
||||
{
|
||||
const char *const comma = strchr (k, ',');
|
||||
if (comma == NULL)
|
||||
*kindName = eStrdup (k);
|
||||
else
|
||||
{
|
||||
*kindName = (char*) eMalloc (comma - k + 1);
|
||||
strncpy (*kindName, k, comma - k);
|
||||
(*kindName) [comma - k] = '\0';
|
||||
k = comma + 1;
|
||||
if (k [0] != '\0')
|
||||
*description = eStrdup (k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void printRegexKind (const regexPattern *pat, unsigned int i, boolean indent)
|
||||
{
|
||||
const struct sKind *const kind = &pat [i].u.tag.kind;
|
||||
const char *const indentation = indent ? " " : "";
|
||||
Assert (pat [i].type == PTRN_TAG);
|
||||
printf ("%s%c %s %s\n", indentation,
|
||||
kind->letter != '\0' ? kind->letter : '?',
|
||||
kind->description != NULL ? kind->description : kind->name,
|
||||
kind->enabled ? "" : " [off]");
|
||||
}
|
||||
|
||||
static void processLanguageRegex (const langType language,
|
||||
const char* const parameter)
|
||||
{
|
||||
if (parameter == NULL || parameter [0] == '\0')
|
||||
clearPatternSet (language);
|
||||
else if (parameter [0] != '@')
|
||||
addLanguageRegex (language, parameter);
|
||||
else if (! doesFileExist (parameter + 1))
|
||||
error (WARNING, "cannot open regex file");
|
||||
else
|
||||
{
|
||||
const char* regexfile = parameter + 1;
|
||||
FILE* const fp = fopen (regexfile, "r");
|
||||
if (fp == NULL)
|
||||
error (WARNING | PERROR, regexfile);
|
||||
else
|
||||
{
|
||||
vString* const regex = vStringNew ();
|
||||
while (readLine (regex, fp))
|
||||
addLanguageRegex (language, vStringValue (regex));
|
||||
fclose (fp);
|
||||
vStringDelete (regex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Regex pattern matching
|
||||
*/
|
||||
|
||||
#if defined (POSIX_REGEX)
|
||||
|
||||
static vString* substitute (
|
||||
const char* const in, const char* out,
|
||||
const int nmatch, const regmatch_t* const pmatch)
|
||||
{
|
||||
vString* result = vStringNew ();
|
||||
const char* p;
|
||||
for (p = out ; *p != '\0' ; p++)
|
||||
{
|
||||
if (*p == '\\' && isdigit ((int) *++p))
|
||||
{
|
||||
const int dig = *p - '0';
|
||||
if (0 < dig && dig < nmatch && pmatch [dig].rm_so != -1)
|
||||
{
|
||||
const int diglen = pmatch [dig].rm_eo - pmatch [dig].rm_so;
|
||||
vStringNCatS (result, in + pmatch [dig].rm_so, diglen);
|
||||
}
|
||||
}
|
||||
else if (*p != '\n' && *p != '\r')
|
||||
vStringPut (result, *p);
|
||||
}
|
||||
vStringTerminate (result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void matchTagPattern (const vString* const line,
|
||||
const regexPattern* const patbuf,
|
||||
const regmatch_t* const pmatch)
|
||||
{
|
||||
vString *const name = substitute (vStringValue (line),
|
||||
patbuf->u.tag.name_pattern, BACK_REFERENCE_COUNT, pmatch);
|
||||
vStringStripLeading (name);
|
||||
vStringStripTrailing (name);
|
||||
if (vStringLength (name) > 0)
|
||||
makeRegexTag (name, &patbuf->u.tag.kind);
|
||||
else
|
||||
error (WARNING, "%s:%ld: null expansion of name pattern \"%s\"",
|
||||
getInputFileName (), getInputLineNumber (),
|
||||
patbuf->u.tag.name_pattern);
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
static void matchCallbackPattern (
|
||||
const vString* const line, const regexPattern* const patbuf,
|
||||
const regmatch_t* const pmatch)
|
||||
{
|
||||
regexMatch matches [BACK_REFERENCE_COUNT];
|
||||
unsigned int count = 0;
|
||||
int i;
|
||||
for (i = 0 ; i < BACK_REFERENCE_COUNT && pmatch [i].rm_so != -1 ; ++i)
|
||||
{
|
||||
matches [i].start = pmatch [i].rm_so;
|
||||
matches [i].length = pmatch [i].rm_eo - pmatch [i].rm_so;
|
||||
++count;
|
||||
}
|
||||
patbuf->u.callback.function (vStringValue (line), matches, count);
|
||||
}
|
||||
|
||||
static boolean matchRegexPattern (const vString* const line,
|
||||
const regexPattern* const patbuf)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
regmatch_t pmatch [BACK_REFERENCE_COUNT];
|
||||
const int match = regexec (patbuf->pattern, vStringValue (line),
|
||||
BACK_REFERENCE_COUNT, pmatch, 0);
|
||||
if (match == 0)
|
||||
{
|
||||
result = TRUE;
|
||||
if (patbuf->type == PTRN_TAG)
|
||||
matchTagPattern (line, patbuf, pmatch);
|
||||
else if (patbuf->type == PTRN_CALLBACK)
|
||||
matchCallbackPattern (line, patbuf, pmatch);
|
||||
else
|
||||
{
|
||||
Assert ("invalid pattern type" == NULL);
|
||||
result = FALSE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* PUBLIC INTERFACE */
|
||||
|
||||
/* Match against all patterns for specified language. Returns true if at least
|
||||
* on pattern matched.
|
||||
*/
|
||||
extern boolean matchRegex (const vString* const line, const langType language)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
if (language != LANG_IGNORE && language <= SetUpper &&
|
||||
Sets [language].count > 0)
|
||||
{
|
||||
const patternSet* const set = Sets + language;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < set->count ; ++i)
|
||||
if (matchRegexPattern (line, set->patterns + i))
|
||||
result = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void findRegexTags (void)
|
||||
{
|
||||
/* merely read all lines of the file */
|
||||
while (fileReadLine () != NULL)
|
||||
;
|
||||
}
|
||||
|
||||
#endif /* HAVE_REGEX */
|
||||
|
||||
extern void addTagRegex (
|
||||
const langType language __unused__,
|
||||
const char* const regex __unused__,
|
||||
const char* const name __unused__,
|
||||
const char* const kinds __unused__,
|
||||
const char* const flags __unused__)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
Assert (regex != NULL);
|
||||
Assert (name != NULL);
|
||||
if (! regexBroken)
|
||||
{
|
||||
regex_t* const cp = compileRegex (regex, flags);
|
||||
if (cp != NULL)
|
||||
{
|
||||
char kind;
|
||||
char* kindName;
|
||||
char* description;
|
||||
parseKinds (kinds, &kind, &kindName, &description);
|
||||
addCompiledTagPattern (language, cp, eStrdup (name),
|
||||
kind, kindName, description);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void addCallbackRegex (
|
||||
const langType language __unused__,
|
||||
const char* const regex __unused__,
|
||||
const char* const flags __unused__,
|
||||
const regexCallback callback __unused__)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
Assert (regex != NULL);
|
||||
if (! regexBroken)
|
||||
{
|
||||
regex_t* const cp = compileRegex (regex, flags);
|
||||
if (cp != NULL)
|
||||
addCompiledCallbackPattern (language, cp, callback);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void addLanguageRegex (
|
||||
const langType language __unused__, const char* const regex __unused__)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
if (! regexBroken)
|
||||
{
|
||||
char *const regex_pat = eStrdup (regex);
|
||||
char *name, *kinds, *flags;
|
||||
if (parseTagRegex (regex_pat, &name, &kinds, &flags))
|
||||
{
|
||||
addTagRegex (language, regex_pat, name, kinds, flags);
|
||||
eFree (regex_pat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Regex option parsing
|
||||
*/
|
||||
|
||||
extern boolean processRegexOption (const char *const option,
|
||||
const char *const parameter __unused__)
|
||||
{
|
||||
boolean handled = FALSE;
|
||||
const char* const dash = strchr (option, '-');
|
||||
if (dash != NULL && strncmp (option, "regex", dash - option) == 0)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
langType language;
|
||||
language = getNamedLanguage (dash + 1);
|
||||
if (language == LANG_IGNORE)
|
||||
error (WARNING, "unknown language in --%s option", option);
|
||||
else
|
||||
processLanguageRegex (language, parameter);
|
||||
#else
|
||||
error (WARNING, "regex support not available; required for --%s option",
|
||||
option);
|
||||
#endif
|
||||
handled = TRUE;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
extern void disableRegexKinds (const langType language __unused__)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
if (language <= SetUpper && Sets [language].count > 0)
|
||||
{
|
||||
patternSet* const set = Sets + language;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < set->count ; ++i)
|
||||
if (set->patterns [i].type == PTRN_TAG)
|
||||
set->patterns [i].u.tag.kind.enabled = FALSE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern boolean enableRegexKind (
|
||||
const langType language __unused__,
|
||||
const int kind __unused__, const boolean mode __unused__)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
#ifdef HAVE_REGEX
|
||||
if (language <= SetUpper && Sets [language].count > 0)
|
||||
{
|
||||
patternSet* const set = Sets + language;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < set->count ; ++i)
|
||||
if (set->patterns [i].type == PTRN_TAG &&
|
||||
set->patterns [i].u.tag.kind.letter == kind)
|
||||
{
|
||||
set->patterns [i].u.tag.kind.enabled = mode;
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void printRegexKinds (const langType language __unused__, boolean indent)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
if (language <= SetUpper && Sets [language].count > 0)
|
||||
{
|
||||
patternSet* const set = Sets + language;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < set->count ; ++i)
|
||||
if (set->patterns [i].type == PTRN_TAG)
|
||||
printRegexKind (set->patterns, i, indent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void freeRegexResources (void)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
int i;
|
||||
for (i = 0 ; i <= SetUpper ; ++i)
|
||||
clearPatternSet (i);
|
||||
if (Sets != NULL)
|
||||
eFree (Sets);
|
||||
Sets = NULL;
|
||||
SetUpper = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check for broken regcomp() on Cygwin */
|
||||
extern void checkRegex (void)
|
||||
{
|
||||
#if defined (HAVE_REGEX) && defined (CHECK_REGCOMP)
|
||||
regex_t patbuf;
|
||||
int errcode;
|
||||
if (regcomp (&patbuf, "/hello/", 0) != 0)
|
||||
{
|
||||
error (WARNING, "Disabling broken regex");
|
||||
regexBroken = TRUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* $Id: lua.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2001, Max Ischenko <[email protected]>.
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for Lua language.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_FUNCTION
|
||||
} luaKind;
|
||||
|
||||
static kindOption LuaKinds [] = {
|
||||
{ TRUE, 'f', "function", "functions" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/* for debugging purposes */
|
||||
static void __unused__ print_string (char *p, char *q)
|
||||
{
|
||||
for ( ; p != q; p++)
|
||||
fprintf (errout, "%c", *p);
|
||||
fprintf (errout, "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function.
|
||||
* Returns 1 if line looks like a line of Lua code.
|
||||
*
|
||||
* TODO: Recognize UNIX bang notation.
|
||||
* (Lua treat first line as a comment if it starts with #!)
|
||||
*
|
||||
*/
|
||||
static boolean is_a_code_line (const unsigned char *line)
|
||||
{
|
||||
boolean result;
|
||||
const unsigned char *p = line;
|
||||
while (isspace ((int) *p))
|
||||
p++;
|
||||
if (p [0] == '\0')
|
||||
result = FALSE;
|
||||
else if (p [0] == '-' && p [1] == '-')
|
||||
result = FALSE;
|
||||
else
|
||||
result = TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void extract_name (const char *begin, const char *end, vString *name)
|
||||
{
|
||||
if (begin != NULL && end != NULL && begin < end)
|
||||
{
|
||||
const char *cp;
|
||||
|
||||
while (isspace ((int) *begin))
|
||||
begin++;
|
||||
while (isspace ((int) *end))
|
||||
end--;
|
||||
if (begin < end)
|
||||
{
|
||||
for (cp = begin ; cp != end; cp++)
|
||||
vStringPut (name, (int) *cp);
|
||||
vStringTerminate (name);
|
||||
|
||||
makeSimpleTag (name, LuaKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void findLuaTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const char *p, *q;
|
||||
|
||||
if (! is_a_code_line (line))
|
||||
continue;
|
||||
|
||||
p = (const char*) strstr ((const char*) line, "function");
|
||||
if (p == NULL)
|
||||
continue;
|
||||
|
||||
q = strchr ((const char*) line, '=');
|
||||
|
||||
if (q == NULL) {
|
||||
p = p + 9; /* skip the `function' word */
|
||||
q = strchr ((const char*) p, '(');
|
||||
extract_name (p, q, name);
|
||||
} else {
|
||||
p = (const char*) &line[0];
|
||||
extract_name (p, q, name);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* LuaParser (void)
|
||||
{
|
||||
static const char* const extensions [] = { "lua", NULL };
|
||||
parserDefinition* def = parserNew ("Lua");
|
||||
def->kinds = LuaKinds;
|
||||
def->kindCount = KIND_COUNT (LuaKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findLuaTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* $Id: mac.c,v 1.2 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2001, Maarten L. Hekkelman
|
||||
*
|
||||
* Author: Maarten L. Hekkelman <[email protected]>
|
||||
* http://www.hekkelman.com
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License. It is provided on an as-is basis and no
|
||||
* responsibility is accepted for its failure to perform as expected.
|
||||
*
|
||||
* This module contains support functions for Exuberant Ctags on Macintosh.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h"
|
||||
|
||||
#include <Files.h>
|
||||
#include <TextUtils.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static int get_path(const char* in_unix_path, unsigned char* out_mac_path)
|
||||
{
|
||||
int l = strlen(in_unix_path);
|
||||
int result = 0;
|
||||
|
||||
if (l > 254)
|
||||
result = -1;
|
||||
else
|
||||
{
|
||||
const char* s = in_unix_path;
|
||||
char *d = (char*)out_mac_path + 1;
|
||||
|
||||
if (*s != '/')
|
||||
*d++ = ':';
|
||||
else
|
||||
++s;
|
||||
|
||||
while (*s)
|
||||
{
|
||||
if (s[0] == '.' && s[1] == '.' && s[2] == '/')
|
||||
{
|
||||
s += 3;
|
||||
*d++ = ':';
|
||||
}
|
||||
else if (s[0] == '.' && s[1] == '/')
|
||||
s += 2;
|
||||
else if (s[0] == '/')
|
||||
{
|
||||
*d++ = ':';
|
||||
|
||||
++s;
|
||||
while (*s == '/')
|
||||
++s;
|
||||
}
|
||||
else
|
||||
*d++ = *s++;
|
||||
}
|
||||
|
||||
out_mac_path[0] = (d - (char*)out_mac_path) - 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DIR *opendir(const char *dirname)
|
||||
{
|
||||
DIR* dirp = (DIR*)calloc(1, sizeof(DIR));
|
||||
|
||||
if (dirp != NULL)
|
||||
{
|
||||
OSErr err;
|
||||
Str255 s;
|
||||
CInfoPBRec pb = { 0 };
|
||||
|
||||
if (strcmp(dirname, "."))
|
||||
{
|
||||
get_path(dirname, s);
|
||||
pb.hFileInfo.ioNamePtr = s;
|
||||
}
|
||||
else
|
||||
pb.hFileInfo.ioNamePtr = NULL;
|
||||
|
||||
err = PBGetCatInfoSync(&pb);
|
||||
if (err != noErr || (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0)
|
||||
{
|
||||
free(dirp);
|
||||
dirp = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirp->file.vRefNum = pb.hFileInfo.ioVRefNum;
|
||||
dirp->file.parID = pb.hFileInfo.ioDirID;
|
||||
dirp->file.name[0] = '\0';
|
||||
dirp->index = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
struct dirent *readdir(DIR *dirp)
|
||||
{
|
||||
if (dirp)
|
||||
{
|
||||
CInfoPBRec pb = { 0 };
|
||||
|
||||
pb.hFileInfo.ioVRefNum = dirp->file.vRefNum;
|
||||
pb.hFileInfo.ioDirID = dirp->file.parID;
|
||||
pb.hFileInfo.ioFDirIndex = dirp->index++;
|
||||
pb.hFileInfo.ioNamePtr = dirp->file.name;
|
||||
|
||||
if (PBGetCatInfoSync(&pb) != noErr)
|
||||
return NULL;
|
||||
|
||||
memcpy(dirp->ent.d_name, dirp->file.name + 1, dirp->file.name[0]);
|
||||
dirp->ent.d_name[dirp->file.name[0]] = 0;
|
||||
return &dirp->ent;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int closedir(DIR *dirp)
|
||||
{
|
||||
if (dirp)
|
||||
free(dirp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rewinddir(DIR *dirp)
|
||||
{
|
||||
if (dirp)
|
||||
dirp->index = 1;
|
||||
}
|
||||
|
||||
int mstat(const char* file, struct stat* st)
|
||||
{
|
||||
CInfoPBRec pb;
|
||||
unsigned char path[256];
|
||||
int result = 0;
|
||||
|
||||
memset(&pb, 0, sizeof(CInfoPBRec));
|
||||
|
||||
if (strcmp(file, ".") == 0)
|
||||
{
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
st->st_mode = S_IFDIR;
|
||||
st->st_ino = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = get_path(file, path);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
pb.hFileInfo.ioNamePtr = path;
|
||||
|
||||
if (PBGetCatInfoSync(&pb) != noErr)
|
||||
result = -1;
|
||||
else
|
||||
{
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
|
||||
if (pb.hFileInfo.ioFlAttrib & ioDirMask)
|
||||
st->st_mode = S_IFDIR;
|
||||
else
|
||||
st->st_mode = S_IFREG;
|
||||
|
||||
st->st_ino = pb.hFileInfo.ioFlStBlk;
|
||||
st->st_dev = pb.hFileInfo.ioVRefNum;
|
||||
st->st_nlink = 1;
|
||||
st->st_size = pb.hFileInfo.ioFlLgLen;
|
||||
st->st_atime = pb.hFileInfo.ioFlMdDat;
|
||||
st->st_mtime = pb.hFileInfo.ioFlMdDat;
|
||||
st->st_ctime = pb.hFileInfo.ioFlCrDat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#undef fopen
|
||||
|
||||
FILE* mfopen(const char* file, const char* mode)
|
||||
{
|
||||
unsigned char path[256];
|
||||
|
||||
if (get_path(file, path) == 0)
|
||||
{
|
||||
int l = path[0];
|
||||
memmove(path, path + 1, l);
|
||||
path[l] = 0;
|
||||
return fopen((char*)path, mode);
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* getcwd(char* out_path, int out_path_len)
|
||||
{
|
||||
OSErr err = noErr;
|
||||
CInfoPBRec pb;
|
||||
FSSpec cwd;
|
||||
|
||||
if (out_path == NULL)
|
||||
{
|
||||
if (out_path_len < PATH_MAX)
|
||||
out_path_len = PATH_MAX;
|
||||
out_path = (char*)malloc(out_path_len);
|
||||
}
|
||||
|
||||
err = FSMakeFSSpec(0, 0, "\p:", &cwd);
|
||||
|
||||
if (cwd.parID == fsRtParID)
|
||||
{
|
||||
*out_path = '/';
|
||||
memcpy(out_path + 1, cwd.name + 1, cwd.name[0]);
|
||||
out_path[1 + cwd.name[0]] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The object isn't a volume */
|
||||
|
||||
/* Is the object a file or a directory? */
|
||||
|
||||
char t[PATH_MAX];
|
||||
char* s;
|
||||
|
||||
s = t + PATH_MAX - cwd.name[0] - 1;
|
||||
memcpy(s, cwd.name + 1, cwd.name[0]);
|
||||
s[cwd.name[0]] = 0;
|
||||
|
||||
/* Get the ancestor directory names */
|
||||
pb.dirInfo.ioNamePtr = cwd.name;
|
||||
pb.dirInfo.ioVRefNum = cwd.vRefNum;
|
||||
pb.dirInfo.ioDrParID = cwd.parID;
|
||||
do /* loop until we have an error or find the root directory */
|
||||
{
|
||||
pb.dirInfo.ioFDirIndex = -1;
|
||||
pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
|
||||
err = PBGetCatInfoSync(&pb);
|
||||
if ( err == noErr )
|
||||
{
|
||||
*--s = '/';
|
||||
s -= cwd.name[0];
|
||||
memcpy(s, cwd.name + 1, cwd.name[0]);
|
||||
}
|
||||
}
|
||||
while (err == noErr && pb.dirInfo.ioDrDirID != fsRtDirID && s > t + 1);
|
||||
|
||||
if (s > t + 1)
|
||||
{
|
||||
*--s = '/';
|
||||
strcpy(out_path, s);
|
||||
}
|
||||
else
|
||||
strcpy(out_path, ".");
|
||||
}
|
||||
|
||||
return out_path;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,21 @@
|
||||
This file contains a patch to the Linux file /usr/share/magic which will
|
||||
allow the "file" command to properly identify tags file producted by
|
||||
Exuberant Ctags. To apply the patch, issue the following command as root:
|
||||
|
||||
patch -p0 < magic.diff
|
||||
|
||||
*** /usr/share/magic.orig Wed Feb 16 19:04:09 2000
|
||||
--- /usr/share/magic Mon Aug 14 20:27:01 2000
|
||||
***************
|
||||
*** 1155,1160 ****
|
||||
--- 1155,1164 ----
|
||||
>84 belong&0x18000000 =0x18000000 undefined fpmode
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
+ # ctags: file (1) magic for Exuberant Ctags files
|
||||
+ 0 string !_TAG Exuberant Ctags tag file
|
||||
+
|
||||
+ #------------------------------------------------------------------------------
|
||||
# database: file(1) magic for various databases
|
||||
#
|
||||
# extracted from header/code files by Graeme Wilford ([email protected])
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* $Id: main.c,v 1.24 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2003, Darren Hiebert
|
||||
*
|
||||
* Author: Darren Hiebert <[email protected]>
|
||||
* http://ctags.sourceforge.net
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License. It is provided on an as-is basis and no
|
||||
* responsibility is accepted for its failure to perform as expected.
|
||||
*
|
||||
* This is a reimplementation of the ctags (1) program. It is an attempt to
|
||||
* provide a fully featured ctags program which is free of the limitations
|
||||
* which most (all?) others are subject to.
|
||||
*
|
||||
* This module contains the start-up code and routines to determine the list
|
||||
* of files to parsed for tags.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* To provide timings features if available.
|
||||
*/
|
||||
#ifdef HAVE_CLOCK
|
||||
# ifdef HAVE_TIME_H
|
||||
# include <time.h>
|
||||
# endif
|
||||
#else
|
||||
# ifdef HAVE_TIMES
|
||||
# ifdef HAVE_SYS_TIMES_H
|
||||
# include <sys/times.h>
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* To provide directory searching for recursion feature.
|
||||
*/
|
||||
#ifdef AMIGA
|
||||
# include <dos/dosasl.h> /* for struct AnchorPath */
|
||||
# include <clib/dos_protos.h> /* function prototypes */
|
||||
# define ANCHOR_BUF_SIZE 512
|
||||
# define ANCHOR_SIZE (sizeof (struct AnchorPath) + ANCHOR_BUF_SIZE)
|
||||
# ifdef __SASC
|
||||
extern struct DosLibrary *DOSBase;
|
||||
# include <pragmas/dos_pragmas.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DIRENT_H
|
||||
# ifdef __BORLANDC__
|
||||
# define boolean BORLAND_boolean
|
||||
# endif
|
||||
# ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h> /* required by dirent.h */
|
||||
# endif
|
||||
# include <dirent.h> /* to declare opendir() */
|
||||
# undef boolean
|
||||
#endif
|
||||
#ifdef HAVE_DIRECT_H
|
||||
# include <direct.h> /* to _getcwd() */
|
||||
#endif
|
||||
#ifdef HAVE_DOS_H
|
||||
# include <dos.h> /* to declare FA_DIREC */
|
||||
#endif
|
||||
#ifdef HAVE_DIR_H
|
||||
# include <dir.h> /* to declare findfirst() and findnext */
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h> /* to declare _findfirst() */
|
||||
#endif
|
||||
|
||||
|
||||
#include "debug.h"
|
||||
#include "keyword.h"
|
||||
#include "main.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define plural(value) (((unsigned long)(value) == 1L) ? "" : "s")
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static struct { long files, lines, bytes; } Totals = { 0, 0, 0 };
|
||||
|
||||
#ifdef AMIGA
|
||||
# include "ctags.h"
|
||||
static const char *VERsion = "$VER: "PROGRAM_NAME" "PROGRAM_VERSION" "
|
||||
# ifdef __SASC
|
||||
__AMIGADATE__
|
||||
# else
|
||||
__DATE__
|
||||
# endif
|
||||
" "AUTHOR_NAME" $";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
static boolean createTagsForEntry (const char *const entryName);
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern void addTotals (
|
||||
const unsigned int files, const long unsigned int lines,
|
||||
const long unsigned int bytes)
|
||||
{
|
||||
Totals.files += files;
|
||||
Totals.lines += lines;
|
||||
Totals.bytes += bytes;
|
||||
}
|
||||
|
||||
extern boolean isDestinationStdout (void)
|
||||
{
|
||||
boolean toStdout = FALSE;
|
||||
|
||||
if (Option.xref || Option.filter ||
|
||||
(Option.tagFileName != NULL && (strcmp (Option.tagFileName, "-") == 0
|
||||
#if defined (VMS)
|
||||
|| strcmp (Option.tagFileName, "sys$output") == 0
|
||||
#else
|
||||
|| strcmp (Option.tagFileName, "/dev/stdout") == 0
|
||||
#endif
|
||||
)))
|
||||
toStdout = TRUE;
|
||||
return toStdout;
|
||||
}
|
||||
|
||||
#if defined (HAVE_OPENDIR)
|
||||
static boolean recurseUsingOpendir (const char *const dirName)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
DIR *const dir = opendir (dirName);
|
||||
if (dir == NULL)
|
||||
error (WARNING | PERROR, "cannot recurse into directory \"%s\"", dirName);
|
||||
else
|
||||
{
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir (dir)) != NULL)
|
||||
{
|
||||
if (strcmp (entry->d_name, ".") != 0 &&
|
||||
strcmp (entry->d_name, "..") != 0)
|
||||
{
|
||||
vString *filePath;
|
||||
if (strcmp (dirName, ".") == 0)
|
||||
filePath = vStringNewInit (entry->d_name);
|
||||
else
|
||||
filePath = combinePathAndFile (dirName, entry->d_name);
|
||||
resize |= createTagsForEntry (vStringValue (filePath));
|
||||
vStringDelete (filePath);
|
||||
}
|
||||
}
|
||||
closedir (dir);
|
||||
}
|
||||
return resize;
|
||||
}
|
||||
|
||||
#elif defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
|
||||
|
||||
static boolean createTagsForWildcardEntry (
|
||||
const char *const pattern, const size_t dirLength,
|
||||
const char *const entryName)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
/* we must not recurse into the directories "." or ".." */
|
||||
if (strcmp (entryName, ".") != 0 && strcmp (entryName, "..") != 0)
|
||||
{
|
||||
vString *const filePath = vStringNew ();
|
||||
vStringNCopyS (filePath, pattern, dirLength);
|
||||
vStringCatS (filePath, entryName);
|
||||
resize = createTagsForEntry (vStringValue (filePath));
|
||||
vStringDelete (filePath);
|
||||
}
|
||||
return resize;
|
||||
}
|
||||
|
||||
static boolean createTagsForWildcardUsingFindfirst (const char *const pattern)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
const size_t dirLength = baseFilename (pattern) - pattern;
|
||||
#if defined (HAVE_FINDFIRST)
|
||||
struct ffblk fileInfo;
|
||||
int result = findfirst (pattern, &fileInfo, FA_DIREC);
|
||||
while (result == 0)
|
||||
{
|
||||
const char *const entry = (const char *) fileInfo.ff_name;
|
||||
resize |= createTagsForWildcardEntry (pattern, dirLength, entry);
|
||||
result = findnext (&fileInfo);
|
||||
}
|
||||
#elif defined (HAVE__FINDFIRST)
|
||||
struct _finddata_t fileInfo;
|
||||
findfirst_t hFile = _findfirst (pattern, &fileInfo);
|
||||
if (hFile != -1L)
|
||||
{
|
||||
do
|
||||
{
|
||||
const char *const entry = (const char *) fileInfo.name;
|
||||
resize |= createTagsForWildcardEntry (pattern, dirLength, entry);
|
||||
} while (_findnext (hFile, &fileInfo) == 0);
|
||||
_findclose (hFile);
|
||||
}
|
||||
#endif
|
||||
return resize;
|
||||
}
|
||||
|
||||
#elif defined (AMIGA)
|
||||
|
||||
static boolean createTagsForAmigaWildcard (const char *const pattern)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
struct AnchorPath *const anchor =
|
||||
(struct AnchorPath *) eMalloc ((size_t) ANCHOR_SIZE);
|
||||
LONG result;
|
||||
|
||||
memset (anchor, 0, (size_t) ANCHOR_SIZE);
|
||||
anchor->ap_Strlen = ANCHOR_BUF_SIZE;
|
||||
/* Allow '.' for current directory */
|
||||
#ifdef APF_DODOT
|
||||
anchor->ap_Flags = APF_DODOT | APF_DOWILD;
|
||||
#else
|
||||
anchor->ap_Flags = APF_DoDot | APF_DoWild;
|
||||
#endif
|
||||
result = MatchFirst ((UBYTE *) pattern, anchor);
|
||||
while (result == 0)
|
||||
{
|
||||
resize |= createTagsForEntry ((char *) anchor->ap_Buf);
|
||||
result = MatchNext (anchor);
|
||||
}
|
||||
MatchEnd (anchor);
|
||||
eFree (anchor);
|
||||
return resize;
|
||||
}
|
||||
#endif
|
||||
|
||||
static boolean recurseIntoDirectory (const char *const dirName)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
if (isRecursiveLink (dirName))
|
||||
verbose ("ignoring \"%s\" (recursive link)\n", dirName);
|
||||
else if (! Option.recurse)
|
||||
verbose ("ignoring \"%s\" (directory)\n", dirName);
|
||||
else
|
||||
{
|
||||
verbose ("RECURSING into directory \"%s\"\n", dirName);
|
||||
#if defined (HAVE_OPENDIR)
|
||||
resize = recurseUsingOpendir (dirName);
|
||||
#elif defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
|
||||
{
|
||||
vString *const pattern = vStringNew ();
|
||||
vStringCopyS (pattern, dirName);
|
||||
vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
|
||||
vStringCatS (pattern, "*.*");
|
||||
resize = createTagsForWildcardUsingFindfirst (vStringValue (pattern));
|
||||
vStringDelete (pattern);
|
||||
}
|
||||
#elif defined (AMIGA)
|
||||
{
|
||||
vString *const pattern = vStringNew ();
|
||||
if (*dirName != '\0' && strcmp (dirName, ".") != 0)
|
||||
{
|
||||
vStringCopyS (pattern, dirName);
|
||||
if (dirName [strlen (dirName) - 1] != '/')
|
||||
vStringPut (pattern, '/');
|
||||
}
|
||||
vStringCatS (pattern, "#?");
|
||||
resize = createTagsForAmigaWildcard (vStringValue (pattern));
|
||||
vStringDelete (pattern);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return resize;
|
||||
}
|
||||
|
||||
static boolean createTagsForEntry (const char *const entryName)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
fileStatus *status = eStat (entryName);
|
||||
|
||||
Assert (entryName != NULL);
|
||||
if (isExcludedFile (entryName))
|
||||
verbose ("excluding \"%s\"\n", entryName);
|
||||
else if (status->isSymbolicLink && ! Option.followLinks)
|
||||
verbose ("ignoring \"%s\" (symbolic link)\n", entryName);
|
||||
else if (! status->exists)
|
||||
error (WARNING | PERROR, "cannot open source file \"%s\"", entryName);
|
||||
else if (status->isDirectory)
|
||||
resize = recurseIntoDirectory (entryName);
|
||||
else if (! status->isNormalFile)
|
||||
verbose ("ignoring \"%s\" (special file)\n", entryName);
|
||||
else
|
||||
resize = parseFile (entryName);
|
||||
|
||||
return resize;
|
||||
}
|
||||
|
||||
#ifdef MANUAL_GLOBBING
|
||||
|
||||
static boolean createTagsForWildcardArg (const char *const arg)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
vString *const pattern = vStringNewInit (arg);
|
||||
char *patternS = vStringValue (pattern);
|
||||
|
||||
#if defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
|
||||
/* We must transform the "." and ".." forms into something that can
|
||||
* be expanded by the findfirst/_findfirst functions.
|
||||
*/
|
||||
if (Option.recurse &&
|
||||
(strcmp (patternS, ".") == 0 || strcmp (patternS, "..") == 0))
|
||||
{
|
||||
vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
|
||||
vStringCatS (pattern, "*.*");
|
||||
}
|
||||
resize |= createTagsForWildcardUsingFindfirst (patternS);
|
||||
#endif
|
||||
vStringDelete (pattern);
|
||||
return resize;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static boolean createTagsForArgs (cookedArgs *const args)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
|
||||
/* Generate tags for each argument on the command line.
|
||||
*/
|
||||
while (! cArgOff (args))
|
||||
{
|
||||
const char *const arg = cArgItem (args);
|
||||
|
||||
#ifdef MANUAL_GLOBBING
|
||||
resize |= createTagsForWildcardArg (arg);
|
||||
#else
|
||||
resize |= createTagsForEntry (arg);
|
||||
#endif
|
||||
cArgForth (args);
|
||||
parseOptions (args);
|
||||
}
|
||||
return resize;
|
||||
}
|
||||
|
||||
/* Read from an opened file a list of file names for which to generate tags.
|
||||
*/
|
||||
static boolean createTagsFromFileInput (FILE *const fp, const boolean filter)
|
||||
{
|
||||
boolean resize = FALSE;
|
||||
if (fp != NULL)
|
||||
{
|
||||
cookedArgs *args = cArgNewFromLineFile (fp);
|
||||
parseOptions (args);
|
||||
while (! cArgOff (args))
|
||||
{
|
||||
resize |= createTagsForEntry (cArgItem (args));
|
||||
if (filter)
|
||||
{
|
||||
if (Option.filterTerminator != NULL)
|
||||
fputs (Option.filterTerminator, stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
cArgForth (args);
|
||||
parseOptions (args);
|
||||
}
|
||||
cArgDelete (args);
|
||||
}
|
||||
return resize;
|
||||
}
|
||||
|
||||
/* Read from a named file a list of file names for which to generate tags.
|
||||
*/
|
||||
static boolean createTagsFromListFile (const char *const fileName)
|
||||
{
|
||||
boolean resize;
|
||||
Assert (fileName != NULL);
|
||||
if (strcmp (fileName, "-") == 0)
|
||||
resize = createTagsFromFileInput (stdin, FALSE);
|
||||
else
|
||||
{
|
||||
FILE *const fp = fopen (fileName, "r");
|
||||
if (fp == NULL)
|
||||
error (FATAL | PERROR, "cannot open list file \"%s\"", fileName);
|
||||
resize = createTagsFromFileInput (fp, FALSE);
|
||||
fclose (fp);
|
||||
}
|
||||
return resize;
|
||||
}
|
||||
|
||||
#if defined (HAVE_CLOCK)
|
||||
# define CLOCK_AVAILABLE
|
||||
# ifndef CLOCKS_PER_SEC
|
||||
# define CLOCKS_PER_SEC 1000000
|
||||
# endif
|
||||
#elif defined (HAVE_TIMES)
|
||||
# define CLOCK_AVAILABLE
|
||||
# define CLOCKS_PER_SEC 60
|
||||
static clock_t clock (void)
|
||||
{
|
||||
struct tms buf;
|
||||
|
||||
times (&buf);
|
||||
return (buf.tms_utime + buf.tms_stime);
|
||||
}
|
||||
#else
|
||||
# define clock() (clock_t)0
|
||||
#endif
|
||||
|
||||
static void printTotals (const clock_t *const timeStamps)
|
||||
{
|
||||
const unsigned long totalTags = TagFile.numTags.added +
|
||||
TagFile.numTags.prev;
|
||||
|
||||
fprintf (errout, "%ld file%s, %ld line%s (%ld kB) scanned",
|
||||
Totals.files, plural (Totals.files),
|
||||
Totals.lines, plural (Totals.lines),
|
||||
Totals.bytes/1024L);
|
||||
#ifdef CLOCK_AVAILABLE
|
||||
{
|
||||
const double interval = ((double) (timeStamps [1] - timeStamps [0])) /
|
||||
CLOCKS_PER_SEC;
|
||||
|
||||
fprintf (errout, " in %.01f seconds", interval);
|
||||
if (interval != (double) 0.0)
|
||||
fprintf (errout, " (%lu kB/s)",
|
||||
(unsigned long) (Totals.bytes / interval) / 1024L);
|
||||
}
|
||||
#endif
|
||||
fputc ('\n', errout);
|
||||
|
||||
fprintf (errout, "%lu tag%s added to tag file",
|
||||
TagFile.numTags.added, plural (TagFile.numTags.added));
|
||||
if (Option.append)
|
||||
fprintf (errout, " (now %lu tags)", totalTags);
|
||||
fputc ('\n', errout);
|
||||
|
||||
if (totalTags > 0 && Option.sorted != SO_UNSORTED)
|
||||
{
|
||||
fprintf (errout, "%lu tag%s sorted", totalTags, plural (totalTags));
|
||||
#ifdef CLOCK_AVAILABLE
|
||||
fprintf (errout, " in %.02f seconds",
|
||||
((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC);
|
||||
#endif
|
||||
fputc ('\n', errout);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf (errout, "longest tag line = %lu\n",
|
||||
(unsigned long) TagFile.max.line);
|
||||
#endif
|
||||
}
|
||||
|
||||
static boolean etagsInclude (void)
|
||||
{
|
||||
return (boolean)(Option.etags && Option.etagsInclude != NULL);
|
||||
}
|
||||
|
||||
static void makeTags (cookedArgs *args)
|
||||
{
|
||||
clock_t timeStamps [3];
|
||||
boolean resize = FALSE;
|
||||
boolean files = (boolean)(! cArgOff (args) || Option.fileList != NULL
|
||||
|| Option.filter);
|
||||
|
||||
if (! files)
|
||||
{
|
||||
if (filesRequired ())
|
||||
error (FATAL, "No files specified. Try \"%s --help\".",
|
||||
getExecutableName ());
|
||||
else if (! Option.recurse && ! etagsInclude ())
|
||||
return;
|
||||
}
|
||||
|
||||
#define timeStamp(n) timeStamps[(n)]=(Option.printTotals ? clock():(clock_t)0)
|
||||
if (! Option.filter)
|
||||
openTagFile ();
|
||||
|
||||
timeStamp (0);
|
||||
|
||||
if (! cArgOff (args))
|
||||
{
|
||||
verbose ("Reading command line arguments\n");
|
||||
resize = createTagsForArgs (args);
|
||||
}
|
||||
if (Option.fileList != NULL)
|
||||
{
|
||||
verbose ("Reading list file\n");
|
||||
resize = (boolean) (createTagsFromListFile (Option.fileList) || resize);
|
||||
}
|
||||
if (Option.filter)
|
||||
{
|
||||
verbose ("Reading filter input\n");
|
||||
resize = (boolean) (createTagsFromFileInput (stdin, TRUE) || resize);
|
||||
}
|
||||
if (! files && Option.recurse)
|
||||
resize = recurseIntoDirectory (".");
|
||||
|
||||
timeStamp (1);
|
||||
|
||||
if (! Option.filter)
|
||||
closeTagFile (resize);
|
||||
|
||||
timeStamp (2);
|
||||
|
||||
if (Option.printTotals)
|
||||
printTotals (timeStamps);
|
||||
#undef timeStamp
|
||||
}
|
||||
|
||||
/*
|
||||
* Start up code
|
||||
*/
|
||||
|
||||
extern int main (int __unused__ argc, char **argv)
|
||||
{
|
||||
cookedArgs *args;
|
||||
#ifdef VMS
|
||||
extern int getredirection (int *ac, char ***av);
|
||||
|
||||
/* do wildcard expansion and I/O redirection */
|
||||
getredirection (&argc, &argv);
|
||||
#endif
|
||||
|
||||
#ifdef AMIGA
|
||||
/* This program doesn't work when started from the Workbench */
|
||||
if (argc == 0)
|
||||
exit (1);
|
||||
#endif
|
||||
|
||||
#ifdef __EMX__
|
||||
_wildcard (&argc, &argv); /* expand wildcards in argument list */
|
||||
#endif
|
||||
|
||||
#if defined (macintosh) && BUILD_MPW_TOOL == 0
|
||||
argc = ccommand (&argv);
|
||||
#endif
|
||||
|
||||
setCurrentDirectory ();
|
||||
setExecutableName (*argv++);
|
||||
checkRegex ();
|
||||
|
||||
args = cArgNewFromArgv (argv);
|
||||
previewFirstOption (args);
|
||||
testEtagsInvocation ();
|
||||
initializeParsing ();
|
||||
initOptions ();
|
||||
readOptionConfiguration ();
|
||||
verbose ("Reading initial options from command line\n");
|
||||
parseOptions (args);
|
||||
checkOptions ();
|
||||
makeTags (args);
|
||||
|
||||
/* Clean up.
|
||||
*/
|
||||
cArgDelete (args);
|
||||
freeKeywordTable ();
|
||||
freeRoutineResources ();
|
||||
freeSourceFileResources ();
|
||||
freeTagFileResources ();
|
||||
freeOptionResources ();
|
||||
freeParserResources ();
|
||||
freeRegexResources ();
|
||||
|
||||
exit (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $Id: main.h,v 1.8 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to main.c
|
||||
*/
|
||||
#ifndef _MAIN_H
|
||||
#define _MAIN_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void addTotals (const unsigned int files, const long unsigned int lines, const long unsigned int bytes);
|
||||
extern boolean isDestinationStdout (void);
|
||||
extern int main (int argc, char **argv);
|
||||
|
||||
#endif /* _MAIN_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,401 @@
|
||||
# $Id: maintainer.mak,v 1.36 2006/05/30 04:37:12 darren Exp $
|
||||
#
|
||||
# Copyright (c) 1996-2002, Darren Hiebert
|
||||
#
|
||||
# Development makefile for Exuberant Ctags, used to build releases.
|
||||
# Requires GNU make.
|
||||
|
||||
OBJEXT = o
|
||||
|
||||
include source.mak
|
||||
|
||||
DSOURCES = $(SOURCES) debug.c
|
||||
|
||||
DOS_VER_FILES= ctags.h ctags.1 ctags.lsm NEWS
|
||||
|
||||
VERSION_FILES= $(DOS_VER_FILES) configure.in ctags.spec
|
||||
|
||||
LIB_FILES = readtags.c readtags.h
|
||||
|
||||
ENVIRONMENT_MAKEFILES = \
|
||||
mk_bc3.mak mk_bc5.mak mk_djg.mak mk_manx.mak mk_ming.mak \
|
||||
mk_mpw.mak mk_mvc.mak mk_os2.mak mk_qdos.mak mk_sas.mak \
|
||||
|
||||
COMMON_FILES = COPYING EXTENDING.html FAQ INSTALL.oth NEWS README \
|
||||
$(ENVIRONMENT_MAKEFILES) source.mak \
|
||||
$(DSOURCES) $(HEADERS) $(LIB_FILES) \
|
||||
$(ENVIRONMENT_SOURCES) $(ENVIRONMENT_HEADERS)
|
||||
|
||||
UNIX_FILES = $(COMMON_FILES) \
|
||||
.indent.pro INSTALL acconfig.h configure.in \
|
||||
Makefile.in maintainer.mak testing.mak \
|
||||
descrip.mms mkinstalldirs magic.diff \
|
||||
ctags.1 ctags.lsm
|
||||
|
||||
DOS_FILES = $(COMMON_FILES)
|
||||
|
||||
CVS_FILES = $(UNIX_FILES)
|
||||
|
||||
WARNINGS = -Wall -W -Wpointer-arith -Wcast-align -Wwrite-strings \
|
||||
-Wmissing-prototypes -Wmissing-declarations \
|
||||
-Wnested-externs -Wcast-qual -Wshadow -pedantic \
|
||||
-Wstrict-prototypes \
|
||||
# -Wtraditional -Wconversion -Werror
|
||||
|
||||
ERRFILE = errors
|
||||
REDIR = 2>&1 | tee $(ERRFILE)
|
||||
|
||||
RPM_ROOT= $(HOME)/Rpm
|
||||
CTAGS_DOSDIR = win32
|
||||
WEB_ARCHIVE_DIR = releases
|
||||
CTAGS_WEBSITE = website
|
||||
DEP_DIR = .deps
|
||||
|
||||
CC = gcc
|
||||
INCLUDE = -I.
|
||||
DEFS = -DHAVE_CONFIG_H
|
||||
COMP_FLAGS = $(INCLUDE) $(DEFS) $(CFLAGS)
|
||||
PROF_OPT= -O3 -march=i686
|
||||
#OPT = $(PROF_OPT) -fomit-frame-pointer
|
||||
OPT = $(PROF_OPT)
|
||||
DCFLAGS = $(COMP_FLAGS) -DDEBUG -DINTERNAL_SORT
|
||||
LD = gcc
|
||||
LDFLAGS =
|
||||
|
||||
readtags.err: DCFLAGS += -DREADTAGS_MAIN
|
||||
|
||||
AUTO_GEN = configure config.h.in
|
||||
CONFIG_GEN = config.cache config.log config.status config.run config.h Makefile
|
||||
PROF_GEN = gmon.out
|
||||
COV_GEN = *.da *.gcov
|
||||
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
ifeq ($(findstring clean,$(MAKECMDGOALS)),)
|
||||
ifneq ($(MAKECMDGOALS),setup)
|
||||
ifeq ($(wildcard config.h),)
|
||||
ctags dctags ctags.prof ctags.cov:
|
||||
$(MAKE) config.h
|
||||
$(MAKE) $(MAKECMDGOALS)
|
||||
else
|
||||
all: dctags tags syntax.vim
|
||||
|
||||
-include $(DSOURCES:%.c=$(DEP_DIR)/%.d) $(DEP_DIR)/readtags.d
|
||||
|
||||
#
|
||||
# Executable targets
|
||||
#
|
||||
ctags: $(SOURCES:.c=.o)
|
||||
@ echo "-- Linking $@"
|
||||
@ $(LD) -o $@ $(LDFLAGS) $^
|
||||
|
||||
dctags: $(SOURCES:.c=.od) debug.od
|
||||
@ echo "-- Building $@"
|
||||
$(LD) -o $@ $(LDFLAGS) $^
|
||||
|
||||
mctags: $(SOURCES:.c=.om) debug.om safe_malloc.om
|
||||
@ echo "-- Building $@"
|
||||
$(LD) -o $@ $(LDFLAGS) $^
|
||||
|
||||
ctags.prof: $(SOURCES) $(HEADERS) Makefile
|
||||
$(CC) -pg $(PROF_OPT) $(COMP_FLAGS) $(WARNINGS) $(SOURCES) -o $@
|
||||
|
||||
ctags.cov: $(SOURCES) $(HEADERS) Makefile
|
||||
$(CC) -fprofile-arcs -ftest-coverage $(COMP_FLAGS) $(WARNINGS) $(SOURCES) -o $@
|
||||
|
||||
gcov: $(SOURCES:.c=.c.gcov)
|
||||
|
||||
readtags: readtags.[ch]
|
||||
$(CC) -g $(COMP_FLAGS) -DDEBUG -DREADTAGS_MAIN -o $@ readtags.c
|
||||
|
||||
readtags.o: readtags.c readtags.h
|
||||
$(CC) $(COMP_FLAGS) -c readtags.c
|
||||
|
||||
etyperef: etyperef.o keyword.o routines.o strlist.o vstring.o
|
||||
$(CC) -o $@ $^
|
||||
|
||||
etyperef.o: eiffel.c
|
||||
$(CC) -DTYPE_REFERENCE_TOOL $(OPT) $(COMP_FLAGS) -o $@ -c $<
|
||||
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ctags32.exe: $(SOURCES) $(HEADERS)
|
||||
gcc-dos -DMSDOS -O2 -Wall -s -o $@ $(SOURCES)
|
||||
|
||||
#
|
||||
# Support targets
|
||||
#
|
||||
FORCE:
|
||||
|
||||
config.h.in: acconfig.h configure.in
|
||||
autoheader
|
||||
@ touch $@
|
||||
|
||||
configure: configure.in
|
||||
autoconf
|
||||
|
||||
config.status: configure
|
||||
./config.status --recheck
|
||||
|
||||
config.h: config.h.in config.status
|
||||
./config.status
|
||||
touch $@
|
||||
|
||||
depclean:
|
||||
rm -f $(DEP_DIR)/*.d
|
||||
|
||||
profclean:
|
||||
rm -f $(PROF_GEN)
|
||||
|
||||
gcovclean:
|
||||
rm -f $(COV_GEN)
|
||||
|
||||
clean: depclean profclean gcovclean clean-test
|
||||
rm -f *.[ois] *.o[dm] ctags dctags mctags ctags*.exe readtags etyperef \
|
||||
ctags.html ctags.prof ctags.cov *.bb *.bbg tags TAGS syntax.vim \
|
||||
$(ERRFILE)
|
||||
|
||||
distclean: clean
|
||||
rm -f $(CONFIG_GEN)
|
||||
|
||||
maintainer-clean maintclean: distclean
|
||||
rm -f $(AUTO_GEN)
|
||||
|
||||
ctags.man: ctags.1
|
||||
groff -Tascii -mandoc $< | sed 's/.//g' > $@
|
||||
|
||||
ctags.html: ctags.1
|
||||
man2html $< > $@
|
||||
|
||||
tags: $(DSOURCES) $(HEADERS) $(LIB_FILES) Makefile *.mak
|
||||
@ echo "-- Building tag file"
|
||||
@ ctags *
|
||||
|
||||
#
|
||||
# Create a Vim syntax file for all typedefs
|
||||
#
|
||||
syntax: syntax.vim
|
||||
syntax.vim: $(DSOURCES) $(HEADERS) $(LIB_FILES)
|
||||
@ echo "-- Generating syntax file"
|
||||
@ ctags --c-types=cgstu --file-scope -o- $^ |\
|
||||
awk '{print $$1}' | sort -u | fmt |\
|
||||
awk '{printf("syntax keyword Typedef\t%s\n", $$0)}' > $@
|
||||
|
||||
#
|
||||
# Testing
|
||||
#
|
||||
include testing.mak
|
||||
|
||||
#
|
||||
# CVS management
|
||||
#
|
||||
status:
|
||||
@ cvs -n -q update
|
||||
|
||||
cvs-retag-%: CVS_TAG_OPTIONS := -F
|
||||
|
||||
cvs-tag-% cvs-retag-%: cvs-tagcheck-%
|
||||
@ echo "---------- Tagging release `echo $* | sed 's/\./_/g'`"
|
||||
@ cvs tag -c $(CVS_TAG_OPTIONS) Ctags-`echo $* | sed 's/\./_/g'`
|
||||
|
||||
cvs-tagcheck-%:
|
||||
@ if test -z "$(CVS_TAG_OPTIONS)"; then \
|
||||
if cvs update -p -r Ctags-`echo $* | sed 's/\./_/g'` maintainer.mak >/dev/null 2>&1 ;then \
|
||||
echo "release-$* already exists; use rerelease-$*" >&2 ;\
|
||||
exit 1 ;\
|
||||
fi ;\
|
||||
fi
|
||||
|
||||
cvs-files:
|
||||
@ls -1 $(CVS_FILES)
|
||||
|
||||
#
|
||||
# Web site files
|
||||
#
|
||||
website-%: website-man-% website-index-% $(CTAGS_WEBSITE)/news.html \
|
||||
$(CTAGS_WEBSITE)/EXTENDING.html
|
||||
:
|
||||
|
||||
website-man-%: ctags.1 Makefile
|
||||
@ echo "---------- Generating $(CTAGS_WEBSITE)/ctags.html"
|
||||
umask 022 ; \
|
||||
man2html $< | sed -e "s/@@VERSION@@/$*/g" \
|
||||
-e 's%<A HREF="mailto:[^"]*">\([^@]*\)@\([^<]*\)</A>%\1\ at \2%' \
|
||||
> $(CTAGS_WEBSITE)/ctags.html
|
||||
|
||||
website-index-%: index.html Makefile
|
||||
@ echo "---------- Generating $(CTAGS_WEBSITE)/index.html"
|
||||
umask 022 ; \
|
||||
sed -e "s/@@VERSION@@/$*/g" \
|
||||
-e "s/@@DOS_VERSION@@/`echo $* | sed 's/\.//g'`/g" \
|
||||
-e "s/@@DATE@@/`date +'%d %B %Y'`/" \
|
||||
$< > $(CTAGS_WEBSITE)/index.html
|
||||
|
||||
$(CTAGS_WEBSITE)/EXTENDING.html: EXTENDING.html
|
||||
@ echo "---------- Generating $(CTAGS_WEBSITE)/EXTENDING.html"
|
||||
cp $< $@ && chmod 644 $@
|
||||
|
||||
$(CTAGS_WEBSITE)/news.html: NEWS Makefile
|
||||
@ echo "---------- Generating $(CTAGS_WEBSITE)/news.html"
|
||||
umask 022 ; \
|
||||
sed -e 's/</\</g' -e 's/>/\>/g' \
|
||||
-e 's@^Current Version:.*$$@<html><head><title>Exuberant Ctags: Change Notes</title></head><body><h1>Change Notes</h1><pre>@' \
|
||||
-e 's@\(^ctags-.* (.*)\)$$@<b>\1</b>@' \
|
||||
-e 's@^vim:.*$$@</pre><hr><a href="http:index.html">Back to <strong>Exuberant Ctags</strong></a></body></html>@' \
|
||||
$< > $@
|
||||
|
||||
#
|
||||
# Release management
|
||||
#
|
||||
ctags-%.lsm: ctags.lsm
|
||||
sed -e "s/@@VERSION@@/$*/" -e "s/@@LSMDATE@@/`date +'%Y-%m-%d'`/" $< > $@
|
||||
|
||||
ctags-%.tar.gz: $(UNIX_FILES) $(VERSION_FILES)
|
||||
@ echo "---------- Building tar ball"
|
||||
if [ -d ctags-$* ] ;then rm -fr ctags-$** ;fi
|
||||
mkdir ctags-$*
|
||||
cp -p $(UNIX_FILES) ctags-$*/
|
||||
for file in $(VERSION_FILES) ;do \
|
||||
rm -f ctags-$*/$${file} ;\
|
||||
sed -e "s/@@VERSION@@/$*/" \
|
||||
-e "s/@@LSMDATE@@/`date +'%Y-%m-%d'`/" \
|
||||
$${file} > ctags-$*/$${file} ;\
|
||||
done
|
||||
chmod 644 ctags-$*/*
|
||||
chmod 755 ctags-$*/mkinstalldirs
|
||||
(cd ctags-$* ;\
|
||||
autoheader ;\
|
||||
chmod 644 config.h.in ;\
|
||||
autoconf ;\
|
||||
chmod 755 configure ;\
|
||||
rm -fr autom4te.cache ;\
|
||||
man2html ctags.1 > ctags.html ;\
|
||||
)
|
||||
tar -zcf $@ ctags-$*
|
||||
|
||||
ctags-%.tar.Z: ctags-%.tar.gz
|
||||
tar -Zcf $@ ctags-$*
|
||||
|
||||
$(CTAGS_DOSDIR)/ctags%: FORCE
|
||||
if [ -d $(CTAGS_DOSDIR)/ctags$* ] ;\
|
||||
then rm -fr $(CTAGS_DOSDIR)/ctags$*/* ;\
|
||||
else mkdir -p $(CTAGS_DOSDIR)/ctags$* ;\
|
||||
fi
|
||||
|
||||
dos1-%: $(DOS_FILES)
|
||||
for file in $^ ;do \
|
||||
unix2dos < $${file} > $(CTAGS_DOSDIR)/ctags$*/$${file} ;\
|
||||
done
|
||||
cd $(CTAGS_DOSDIR); sed -e "s/@@DOS_VERSION@@/$*/" makefile.in > makefile
|
||||
|
||||
dos2-%: $(DOS_VER_FILES)
|
||||
for file in $^ ;do \
|
||||
rm -f $(CTAGS_DOSDIR)/ctags`echo $*|sed 's/\.//g'`/$${file} ;\
|
||||
sed -e "s/@@VERSION@@/$*/" \
|
||||
-e "s/@@LSMDATE@@/`date +'%d%b%y' | tr 'a-z' 'A-Z'`/" $${file} |\
|
||||
unix2dos > $(CTAGS_DOSDIR)/ctags`echo $*|sed 's/\.//g'`/$${file} ;\
|
||||
done ;\
|
||||
cd $(CTAGS_DOSDIR)/ctags`echo $*|sed 's/\.//g'` ; man2html ctags.1 > ctags.html
|
||||
|
||||
dos-%:
|
||||
@ echo "---------- Building MSDOS release directory"
|
||||
$(MAKE) $(CTAGS_DOSDIR)/ctags`echo $*|sed 's/\.//g'` \
|
||||
dos1-`echo $*|sed 's/\.//g'` dos2-$*
|
||||
|
||||
$(RPM_ROOT)/SOURCES $(RPM_ROOT)/SPECS:
|
||||
mkdir -p $@
|
||||
|
||||
rpm-%: ctags-%.tar.gz ctags.spec $(RPM_ROOT)/SOURCES $(RPM_ROOT)/SPECS
|
||||
@ echo "---------- Building RPM"
|
||||
cp -p ctags-$*.tar.gz $(RPM_ROOT)/SOURCES/
|
||||
sed -e "s/@@VERSION@@/$*/" ctags.spec > $(RPM_ROOT)/SPECS/ctags-$*.spec
|
||||
(cd $(RPM_ROOT)/SPECS; CC=gcc rpmbuild -ba ctags-$*.spec)
|
||||
rm -fr $(RPM_ROOT)/BUILD/ctags-$*
|
||||
|
||||
ctags32-%: ctags-%.tar.gz
|
||||
@ echo "---------- Building DPMS binary for MSDOS"
|
||||
(cd ctags-$*; $(MAKE) -f ../Makefile ctags32.exe; mv ctags32.exe ..)
|
||||
rm -f $(CTAGS_DOSDIR)/ctags32.exe
|
||||
mcopy ctags32.exe $(CTAGS_DOSDIR)
|
||||
|
||||
#
|
||||
# Prevent make from deleting these automatically
|
||||
#
|
||||
.PRECIOUS: ctags-%.tar.gz ctags-%.tar.Z
|
||||
|
||||
cleanrelease-%:
|
||||
rm -f ctags-$*.tar.gz
|
||||
rm -fr ctags-$*
|
||||
rm -fr $(CTAGS_DOSDIR)/ctags`echo $*|sed 's/\.//g'`
|
||||
rm -f $(RPM_ROOT)/SOURCES/ctags-$*.tar.gz
|
||||
rm -f $(RPM_ROOT)/RPMS/i386/ctags-$*-1.i386.rpm
|
||||
rm -f $(RPM_ROOT)/SRPMS/ctags-$*-1.src.rpm
|
||||
rm -f $(RPM_ROOT)/SPECS/ctags-$*.spec
|
||||
|
||||
internal-release-%: ctags-%.tar.gz dos-% rpm-% website-%
|
||||
@ echo "---------- Copying files to web archive"
|
||||
cp -p ctags-$*.tar.* $(WEB_ARCHIVE_DIR)
|
||||
cp -p $(RPM_ROOT)/RPMS/i386/ctags-$*-1.i386.rpm $(WEB_ARCHIVE_DIR)
|
||||
cp -p $(RPM_ROOT)/SRPMS/ctags-$*-1.src.rpm $(WEB_ARCHIVE_DIR)
|
||||
cp -p ctags-$*/ctags.lsm $(WEB_ARCHIVE_DIR)/ctags-$*.lsm
|
||||
chmod o+r $(WEB_ARCHIVE_DIR)/*
|
||||
@ echo "---------- Release $* completed"
|
||||
|
||||
release-%: cvs-tag-% internal-release-%
|
||||
:
|
||||
|
||||
rerelease-%: cvs-retag-% internal-release-%
|
||||
:
|
||||
|
||||
#
|
||||
# Dependency file generation
|
||||
#
|
||||
$(DEP_DIR)/%.d: %.c maintainer.mak
|
||||
@ if [ ! -d $(DEP_DIR) ] ;then mkdir -p $(DEP_DIR) ;fi
|
||||
@ $(CC) -M $(DCFLAGS) $< | sed 's/\($*\.o\)\([ :]\)/\1 $*.od $*.om $(@F)\2/g' > $@
|
||||
|
||||
|
||||
%.inc: %.c Makefile
|
||||
-@ $(CC) -MM $(DCFLAGS) $<
|
||||
|
||||
#
|
||||
# Compilation rules
|
||||
#
|
||||
%.o: %.c
|
||||
@ echo "-- Compiling $<"
|
||||
@ $(CC) $(COMP_FLAGS) -DEXTERNAL_SORT $(OPT) $(WARNINGS) -Wuninitialized -c $<
|
||||
|
||||
%.od: %.c
|
||||
@ echo "-- Compiling (debug) $<"
|
||||
@ $(CC) -g $(DCFLAGS) $(WARNINGS) -o $*.od -c $<
|
||||
|
||||
%.om: %.c
|
||||
@ echo "-- Compiling (safe alloc) $<"
|
||||
@ $(CC) -g -DTRAP_MEMORY_CALLS $(DCFLAGS) $(WARNINGS) -o $*.om -c $<
|
||||
|
||||
%.i: %.c FORCE
|
||||
$(CC) $(DCFLAGS) $(WARNINGS) -Wuninitialized -O -E $< > $@
|
||||
|
||||
%.ic: %.c FORCE
|
||||
$(CC) $(DCFLAGS) $(WARNINGS) -Wuninitialized -O -E $< | noblanks > $@
|
||||
|
||||
%.s: %.c FORCE
|
||||
$(CC) $(DCFLAGS) $(WARNINGS) -S $< > $@
|
||||
|
||||
%.err: %.c
|
||||
@ $(CC) $(DCFLAGS) $(WARNINGS) -Wuninitialized -O -c $<
|
||||
@ rm $*.o
|
||||
|
||||
%.c.gcov: %.da
|
||||
@ gcov $*.c
|
||||
|
||||
%.sproto: %.c
|
||||
@ genproto -s -m __ARGS $<
|
||||
|
||||
%.proto: %.c
|
||||
@ genproto -e -m __ARGS $<
|
||||
|
||||
# vi:ts=4 sw=4
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* $Id: make.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2005, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for makefiles.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_MACRO
|
||||
} shKind;
|
||||
|
||||
static kindOption MakeKinds [] = {
|
||||
{ TRUE, 'm', "macro", "macros"}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static int nextChar (void)
|
||||
{
|
||||
int c = fileGetc ();
|
||||
if (c == '\\')
|
||||
{
|
||||
c = fileGetc ();
|
||||
if (c == '\n')
|
||||
c = fileGetc ();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void skipLine (void)
|
||||
{
|
||||
int c;
|
||||
do
|
||||
c = nextChar ();
|
||||
while (c != EOF && c != '\n');
|
||||
if (c == '\n')
|
||||
fileUngetc (c);
|
||||
}
|
||||
|
||||
static int skipToNonWhite (void)
|
||||
{
|
||||
int c;
|
||||
do
|
||||
c = nextChar ();
|
||||
while (c != '\n' && isspace (c));
|
||||
return c;
|
||||
}
|
||||
|
||||
static boolean isIdentifier (int c)
|
||||
{
|
||||
return (boolean)(c != '\0' && (isalnum (c) || strchr (".-_", c) != NULL));
|
||||
}
|
||||
|
||||
static void readIdentifier (const int first, vString *const id)
|
||||
{
|
||||
int c = first;
|
||||
vStringClear (id);
|
||||
while (isIdentifier (c))
|
||||
{
|
||||
vStringPut (id, c);
|
||||
c = nextChar ();
|
||||
}
|
||||
fileUngetc (c);
|
||||
vStringTerminate (id);
|
||||
}
|
||||
|
||||
static void skipToMatch (const char *const pair)
|
||||
{
|
||||
const int begin = pair [0], end = pair [1];
|
||||
const unsigned long inputLineNumber = getInputLineNumber ();
|
||||
int matchLevel = 1;
|
||||
int c = '\0';
|
||||
|
||||
while (matchLevel > 0)
|
||||
{
|
||||
c = nextChar ();
|
||||
if (c == begin)
|
||||
++matchLevel;
|
||||
else if (c == end)
|
||||
--matchLevel;
|
||||
else if (c == '\n')
|
||||
break;
|
||||
}
|
||||
if (c == EOF)
|
||||
verbose ("%s: failed to find match for '%c' at line %lu\n",
|
||||
getInputFileName (), begin, inputLineNumber);
|
||||
}
|
||||
|
||||
static void findMakeTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
boolean newline = TRUE;
|
||||
boolean in_define = FALSE;
|
||||
boolean in_rule = FALSE;
|
||||
boolean variable_possible = TRUE;
|
||||
int c;
|
||||
|
||||
while ((c = nextChar ()) != EOF)
|
||||
{
|
||||
if (newline)
|
||||
{
|
||||
if (in_rule)
|
||||
{
|
||||
if (c == '\t')
|
||||
{
|
||||
skipLine (); /* skip rule */
|
||||
continue;
|
||||
}
|
||||
else
|
||||
in_rule = FALSE;
|
||||
}
|
||||
variable_possible = (boolean)(!in_rule);
|
||||
newline = FALSE;
|
||||
}
|
||||
if (c == '\n')
|
||||
newline = TRUE;
|
||||
else if (isspace (c))
|
||||
continue;
|
||||
else if (c == '#')
|
||||
skipLine ();
|
||||
else if (c == '(')
|
||||
skipToMatch ("()");
|
||||
else if (c == '{')
|
||||
skipToMatch ("{}");
|
||||
else if (c == ':')
|
||||
{
|
||||
variable_possible = TRUE;
|
||||
in_rule = TRUE;
|
||||
}
|
||||
else if (variable_possible && isIdentifier (c))
|
||||
{
|
||||
readIdentifier (c, name);
|
||||
if (strcmp (vStringValue (name), "endef") == 0)
|
||||
in_define = FALSE;
|
||||
else if (in_define)
|
||||
skipLine ();
|
||||
else if (strcmp (vStringValue (name), "define") == 0 &&
|
||||
isIdentifier (c))
|
||||
{
|
||||
in_define = TRUE;
|
||||
c = skipToNonWhite ();
|
||||
readIdentifier (c, name);
|
||||
makeSimpleTag (name, MakeKinds, K_MACRO);
|
||||
skipLine ();
|
||||
}
|
||||
else {
|
||||
c = skipToNonWhite ();
|
||||
if (strchr (":?+", c) != NULL)
|
||||
{
|
||||
boolean append = (boolean)(c == '+');
|
||||
if (c == ':')
|
||||
in_rule = TRUE;
|
||||
c = nextChar ();
|
||||
if (c != '=')
|
||||
fileUngetc (c);
|
||||
else if (append)
|
||||
{
|
||||
skipLine ();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c == '=')
|
||||
{
|
||||
makeSimpleTag (name, MakeKinds, K_MACRO);
|
||||
in_rule = FALSE;
|
||||
skipLine ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
variable_possible = FALSE;
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* MakefileParser (void)
|
||||
{
|
||||
static const char *const patterns [] = { "[Mm]akefile", NULL };
|
||||
static const char *const extensions [] = { "mak", "mk", NULL };
|
||||
parserDefinition* const def = parserNew ("Make");
|
||||
def->kinds = MakeKinds;
|
||||
def->kindCount = KIND_COUNT (MakeKinds);
|
||||
def->patterns = patterns;
|
||||
def->extensions = extensions;
|
||||
def->parser = findMakeTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,46 @@
|
||||
# $Id: mk_bc3.mak,v 1.2 2003/02/24 02:27:53 darren Exp $
|
||||
#
|
||||
# Simple makefile for Borland C++ 3.1
|
||||
|
||||
!include source.mak
|
||||
|
||||
# Adjust the paths to your location of the borland C files
|
||||
BCCLOC = c:\borlandc
|
||||
CC = $(BCCLOC)\bin\bcc
|
||||
INC = -I$(BCCLOC)\include
|
||||
LIB = -L$(BCCLOC)\lib
|
||||
|
||||
# Add this file for wildcard expansion (does NOT work with 4.0!)
|
||||
#EXTRA = $(BCCLOC)\lib\wildargs.obj
|
||||
|
||||
# The following compile options can be changed for better machines.
|
||||
# replace -1- with -2 to produce code for a 80286 or higher
|
||||
# replace -1- with -3 to produce code for a 80386 or higher
|
||||
# add -v for source debugging
|
||||
OPTIMIZE= -1- -O1
|
||||
|
||||
CFLAGS = -DMSDOS -ml -d -w-ccc -w-par -w-pia -w-rch -w-sus $(INC)
|
||||
LFLAGS = $(LIB) $(EXTRA)
|
||||
EXTRA_LIBS =
|
||||
|
||||
ctags.exe: $(SOURCES) respbc3
|
||||
$(CC) $(OPTIMIZE) -e$@ @respbc3
|
||||
|
||||
debug: dctags.exe
|
||||
|
||||
dctags.exe: $(SOURCES) respbc3 debug.c
|
||||
$(CC) -DDEBUG -v -e$@ @respbc3 debug.c
|
||||
|
||||
respbc3: mk_bc3.mak
|
||||
copy &&|
|
||||
$(CFLAGS)
|
||||
$(LFLAGS)
|
||||
$(SOURCES)
|
||||
$(EXTRA_LIBS)
|
||||
| $@
|
||||
|
||||
clean:
|
||||
del *.exe
|
||||
del *.obj
|
||||
del respbc3
|
||||
del tags
|
||||
@@ -0,0 +1,76 @@
|
||||
# $Id: mk_bc5.mak,v 1.5 2003/09/19 04:02:39 darren Exp $
|
||||
#
|
||||
# Makefile for Win32 using Borland C++ compiler, version 5.5 (free version)
|
||||
|
||||
!include source.mak
|
||||
|
||||
BCC = bcc32
|
||||
|
||||
# You can obtain an Win32 version of the Gnu regex support library from
|
||||
# http://people.delphiforums.com/gjc/gnu_regex.html
|
||||
# Point REGEX_DIR to the directory created when you extract the archive.
|
||||
# If you just run gnu_regex.exe in this directory, then you can just
|
||||
# uncomment the REGEX_DIR macro below and everything should work.
|
||||
|
||||
#REGEX_DIR = gnu_regex_dist
|
||||
|
||||
!ifdef REGEX_DIR
|
||||
EXTRA_INC = -I$(REGEX_DIR)
|
||||
|
||||
# Uncomment the following macro to dynamically link against the regex DLL;
|
||||
# otherwise link statically against regex.
|
||||
# WARNING: Borland C++ does not successfully link dynamically.
|
||||
|
||||
#REGEX_DLL=1
|
||||
|
||||
!ifdef REGEX_DLL
|
||||
REGEX_DEFINE = -DHAVE_REGCOMP
|
||||
EXTRA_LIBS = $(REGEX_DIR)\gnu_regex.lib
|
||||
LDFLAGS = -tWCR
|
||||
!else
|
||||
REGEX_DEFINE = -DHAVE_REGCOMP -DREGEX_MALLOC -DSTDC_HEADERS=1
|
||||
EXTRA_LIBS = regex.obj
|
||||
!endif
|
||||
!endif
|
||||
|
||||
DEFINES = -DWIN32 $(REGEX_DEFINE)
|
||||
INCLUDES = $(EXTRA_INC)
|
||||
WARNINGS = -w-aus -w-par -w-pia -w-pro -w-sus
|
||||
CFLAGS = -d -DSTRICT -lTpe -lap
|
||||
|
||||
# Optimizations if your platform supports all of them.
|
||||
OPT = -O2 -OS -lGt
|
||||
|
||||
# Allows multithreading
|
||||
#MT_OPT = -tWM -lcw32mt
|
||||
|
||||
ctags: ctags.exe
|
||||
|
||||
ctags.exe: $(SOURCES) respbc5 $(EXTRA_LIBS)
|
||||
$(BCC) $(OPT) $(MT_OPT) -e$@ $(LDFLAGS) @respbc5
|
||||
|
||||
readtags.exe: readtags.c
|
||||
$(BCC) $(CFLAGS) $(OPT) $(MT_OPT) -e$@ $(DEFINES) -DREADTAGS_MAIN readtags.c $(LDFLAGS)
|
||||
|
||||
# Debug version
|
||||
dctags.exe: $(SOURCES) respbc5 $(EXTRA_LIBS)
|
||||
$(BCC) -DDEBUG -e$@ $(LDFLAGS) @respbc5 debug.c
|
||||
|
||||
regex.obj:
|
||||
$(BCC) -c -o$@ -w- $(DEFINES) -Dconst= $(INCLUDES) $(REGEX_DIR)\regex.c
|
||||
|
||||
respbc5: $(SOURCES) $(HEADERS) mk_bc5.mak
|
||||
echo $(DEFINES) $(INCLUDES) > $@
|
||||
echo $(WARNINGS) >> $@
|
||||
echo $(CFLAGS) >> $@
|
||||
echo $(SOURCES) $(EXTRA_LIBS) >> $@
|
||||
|
||||
mostlyclean:
|
||||
- del *.obj
|
||||
- del *.tds
|
||||
- del dctags.exe
|
||||
- del respbc5
|
||||
- del tags
|
||||
|
||||
clean: mostlyclean
|
||||
- del ctags.exe
|
||||
@@ -0,0 +1,18 @@
|
||||
# $Id: mk_djg.mak,v 1.2 2003/03/31 04:53:22 darren Exp $
|
||||
#
|
||||
# The most simplistic Makefile, for DJGPP Version 2 on Windows
|
||||
#
|
||||
# Rather than using this makefile, it is preferable to run "configure", then
|
||||
# "make" under BASH on DJGPP (i.e. the standard means of building a package on
|
||||
# Unix), but you have to have a fuller complement of DJGPP packages installed
|
||||
# to do this.
|
||||
|
||||
include source.mak
|
||||
|
||||
CFLAGS = -O2 -Wall -DMSDOS
|
||||
|
||||
ctags.exe: $(SOURCES)
|
||||
gcc $(CFLAGS) -s -o ctags.exe $(SOURCES) -lpc
|
||||
|
||||
clean:
|
||||
del ctags.exe
|
||||
@@ -0,0 +1,65 @@
|
||||
# $Id: mk_manx.mak,v 1.9 2003/02/13 02:59:30 darren Exp $
|
||||
#
|
||||
# Makefile for ctags on the Amiga, using Aztec/Manx C 5.0 or later
|
||||
|
||||
OBJEXT = o
|
||||
|
||||
OBJECTS = \
|
||||
args.$(OBJEXT) \
|
||||
asm.$(OBJEXT) \
|
||||
asp.$(OBJEXT) \
|
||||
awk.$(OBJEXT) \
|
||||
beta.$(OBJEXT) \
|
||||
c.$(OBJEXT) \
|
||||
cobol.$(OBJEXT) \
|
||||
eiffel.$(OBJEXT) \
|
||||
entry.$(OBJEXT) \
|
||||
erlang.$(OBJEXT) \
|
||||
fortran.$(OBJEXT) \
|
||||
get.$(OBJEXT) \
|
||||
keyword.$(OBJEXT) \
|
||||
lisp.$(OBJEXT) \
|
||||
lregex.$(OBJEXT) \
|
||||
lua.$(OBJEXT) \
|
||||
main.$(OBJEXT) \
|
||||
make.$(OBJEXT) \
|
||||
options.$(OBJEXT) \
|
||||
parse.$(OBJEXT) \
|
||||
pascal.$(OBJEXT) \
|
||||
perl.$(OBJEXT) \
|
||||
php.$(OBJEXT) \
|
||||
python.$(OBJEXT) \
|
||||
read.$(OBJEXT) \
|
||||
rexx.$(OBJEXT) \
|
||||
routines.$(OBJEXT) \
|
||||
ruby.$(OBJEXT) \
|
||||
scheme.$(OBJEXT) \
|
||||
sh.$(OBJEXT) \
|
||||
slang.$(OBJEXT) \
|
||||
sort.$(OBJEXT) \
|
||||
sml.$(OBJEXT) \
|
||||
sql.$(OBJEXT) \
|
||||
strlist.$(OBJEXT) \
|
||||
tcl.$(OBJEXT) \
|
||||
verilog.$(OBJEXT) \
|
||||
vim.$(OBJEXT) \
|
||||
vstring.$(OBJEXT) \
|
||||
yacc.$(OBJEXT)
|
||||
|
||||
CC = cc
|
||||
|
||||
#>>>>> choose between debugging (-bs) or optimizing (-so)
|
||||
OPTIONS = -so
|
||||
#OPTIONS = -bs
|
||||
|
||||
#>>>>>> choose -g for debugging
|
||||
LN_DEBUG =
|
||||
#LN_DEBUG = -g
|
||||
|
||||
CFLAGS = $(OPTIONS) -wapruq -ps -qf -DAMIGA -Dconst=
|
||||
|
||||
Ctags: $(OBJECTS)
|
||||
ln +q -m $(LN_DEBUG) -o Ctags $(OBJECTS) -lc16 -lm16
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -o $*.o $*.c
|
||||
@@ -0,0 +1,26 @@
|
||||
# $Id: mk_ming.mak,v 1.1 2001/11/02 04:53:43 darren Exp $
|
||||
#
|
||||
# Makefile for Exuberant Ctags under Win32 with Mingw32 compiler
|
||||
#
|
||||
# Note that only the crtdll variant of the compiler is supported.
|
||||
# The msvcrt variant has broken implementations of fgetpos() and fsetpos().
|
||||
|
||||
include source.mak
|
||||
|
||||
CFLAGS = -Wall
|
||||
DEFINES = -DWIN32
|
||||
CC = gcc
|
||||
|
||||
ctags: ctags.exe
|
||||
|
||||
ctags.exe: OPT = -O4
|
||||
ctags.exe: LDFLAGS = -s
|
||||
dctags.exe: OPT = -g
|
||||
|
||||
ctags.exe dctags.exe: $(SOURCES)
|
||||
$(CC) $(LDFLAGS) $(OPT) $(CFLAGS) $(DEFINES) -o $@ $(SOURCES)
|
||||
|
||||
clean:
|
||||
- del ctags.exe
|
||||
- del dctags.exe
|
||||
- del tags
|
||||
@@ -0,0 +1,130 @@
|
||||
# $Id: mk_mpw.mak,v 1.9 2003/02/13 02:59:30 darren Exp $
|
||||
#
|
||||
# Makefile for Macintosh using MPW
|
||||
#
|
||||
# Created by: Maarten L. Hekkelman <[email protected]>
|
||||
|
||||
HEADERS = ¶
|
||||
args.h ctags.h debug.h entry.h general.h get.h keyword.h ¶
|
||||
main.h options.h parse.h parsers.h read.h routines.h sort.h ¶
|
||||
strlist.h vstring.h mac.h
|
||||
|
||||
SOURCES = ¶
|
||||
args.c ¶
|
||||
asm.c ¶
|
||||
asp.c ¶
|
||||
awk.c ¶
|
||||
beta.c ¶
|
||||
c.c ¶
|
||||
cobol.c ¶
|
||||
eiffel.c ¶
|
||||
entry.c ¶
|
||||
erlang.c ¶
|
||||
fortran.c ¶
|
||||
get.c ¶
|
||||
keyword.c ¶
|
||||
lisp.c ¶
|
||||
lregex.c ¶
|
||||
lua.c ¶
|
||||
main.c ¶
|
||||
make.c ¶
|
||||
options.c ¶
|
||||
parse.c ¶
|
||||
pascal.c ¶
|
||||
perl.c ¶
|
||||
php.c ¶
|
||||
python.c ¶
|
||||
read.c ¶
|
||||
rexx.c ¶ ¶
|
||||
routines.c ¶
|
||||
ruby.c ¶
|
||||
scheme.c ¶
|
||||
sh.c ¶
|
||||
slang.c ¶
|
||||
sort.c ¶
|
||||
sml.c ¶
|
||||
sql.c ¶
|
||||
strlist.c ¶
|
||||
tcl.c ¶
|
||||
verilog.c ¶
|
||||
vim.c ¶
|
||||
vstring.c ¶
|
||||
yacc.c ¶
|
||||
mac.c
|
||||
|
||||
OBJ = ¶
|
||||
args.o ¶
|
||||
asm.o ¶
|
||||
asp.o ¶
|
||||
awk.o ¶
|
||||
beta.o ¶
|
||||
c.o ¶
|
||||
cobol.o ¶
|
||||
eiffel.o ¶
|
||||
entry.o ¶
|
||||
erlang.o ¶
|
||||
fortran.o ¶
|
||||
get.o ¶
|
||||
keyword.o ¶
|
||||
lisp.o ¶
|
||||
lregex.o ¶
|
||||
lua.o ¶
|
||||
main.o ¶
|
||||
make.o ¶
|
||||
options.o ¶
|
||||
parse.o ¶
|
||||
pascal.o ¶
|
||||
perl.o ¶
|
||||
php.o ¶
|
||||
python.o ¶
|
||||
read.o ¶
|
||||
rexx.o ¶
|
||||
routines.o ¶
|
||||
ruby.o ¶
|
||||
scheme.o ¶
|
||||
sh.o ¶
|
||||
slang.o ¶
|
||||
sort.o ¶
|
||||
sml.o ¶
|
||||
sql.o ¶
|
||||
strlist.o ¶
|
||||
tcl.o ¶
|
||||
verilog.o ¶
|
||||
vim.o ¶
|
||||
vstring.o ¶
|
||||
yacc.o ¶
|
||||
mac.o
|
||||
|
||||
LIBS = ¶
|
||||
{PPCLibraries}PPCToolLibs.o ¶
|
||||
{SharedLibraries}MathLib ¶
|
||||
{SharedLibraries}InterfaceLib ¶
|
||||
{SharedLibraries}StdCLib ¶
|
||||
{MWPPCLibraries}'MSL StdCRuntime.Lib'
|
||||
|
||||
CC = mwcppc
|
||||
LD = mwlinkppc
|
||||
|
||||
# Using -nodefaults to avoid having {MWCIncludes} in our include paths
|
||||
# Needed since we're building a MPW Tool and not an application.
|
||||
COptions = -nodefaults -i : -i- -i {CIncludes} -opt full
|
||||
LOptions = -xm m -stacksize 128
|
||||
|
||||
all Ä CTags
|
||||
|
||||
CTags Ä TurnOfEcho {OBJ}
|
||||
{LD} {LOptions} -o CTags {OBJ} {LIBS}
|
||||
|
||||
{OBJ} Ä {HEADERS}
|
||||
|
||||
tags Ä CTags
|
||||
:CTags -p. {SOURCES} {HEADERS}
|
||||
|
||||
clean Ä
|
||||
Delete -y -i {OBJ} {CTags} tags
|
||||
|
||||
.o Ä .c
|
||||
{CC} {depDir}{default}.c -o {targDir}{default}.o {COptions}
|
||||
|
||||
TurnOfEcho Ä
|
||||
set echo 0
|
||||
@@ -0,0 +1,61 @@
|
||||
# $Id: mk_mvc.mak,v 1.7 2003/09/19 04:02:39 darren Exp $
|
||||
#
|
||||
# Makefile for Win32 using Microsoft Visual C++ compiler
|
||||
|
||||
include source.mak
|
||||
|
||||
# You can obtain an Win32 version of the Gnu regex support library from
|
||||
# http://people.delphiforums.com/gjc/gnu_regex.html
|
||||
# Point REGEX_DIR to the directory created when you extract the archive.
|
||||
# If you just run gnu_regex.exe in this directory, then you can just
|
||||
# uncomment the REGEX_DIR macro below and everything should work.
|
||||
|
||||
#REGEX_DIR = gnu_regex_dist
|
||||
|
||||
!ifdef REGEX_DIR
|
||||
EXTRA_INC = -I$(REGEX_DIR)
|
||||
|
||||
# Uncomment the following macro to dynamically link against the regex DLL;
|
||||
# otherwise link statically against regex.
|
||||
|
||||
#REGEX_DLL=1
|
||||
|
||||
!ifdef REGEX_DLL
|
||||
REGEX_DEFINE = -DHAVE_REGCOMP
|
||||
EXTRA_LIBS = $(REGEX_DIR)\gnu_regex.lib
|
||||
!else
|
||||
REGEX_DEFINE = -DHAVE_REGCOMP -DREGEX_MALLOC -DSTDC_HEADERS=1
|
||||
EXTRA_LIBS = regex.obj
|
||||
!endif
|
||||
!endif
|
||||
|
||||
DEFINES = -DWIN32 $(REGEX_DEFINE)
|
||||
INCLUDES = $(EXTRA_INC)
|
||||
OPT = /O2 /G5
|
||||
|
||||
ctags: ctags.exe
|
||||
|
||||
ctags.exe: $(SOURCES) respmvc $(EXTRA_LIBS)
|
||||
cl $(OPT) /Fe$@ @respmvc /link setargv.obj
|
||||
|
||||
readtags.exe: readtags.c
|
||||
cl /clr $(OPT) /Fe$@ $(DEFINES) -DREADTAGS_MAIN readtags.c /link setargv.obj
|
||||
|
||||
# Debug version
|
||||
dctags.exe: $(SOURCES) respmvc $(EXTRA_LIBS)
|
||||
cl /Zi -DDEBUG /Fe$@ @respmvc debug.c /link setargv.obj
|
||||
|
||||
regex.obj:
|
||||
cl /c $(OPT) /Fo$@ $(DEFINES) -Dconst= $(INCLUDES) $(REGEX_DIR)\regex.c
|
||||
|
||||
respmvc: $(SOURCES) $(HEADERS) mk_mvc.mak
|
||||
echo $(DEFINES) $(INCLUDES) $(SOURCES) $(EXTRA_LIBS) > $@
|
||||
|
||||
mostlyclean:
|
||||
- del *.obj
|
||||
- del dctags.exe
|
||||
- del respmvc
|
||||
- del tags
|
||||
|
||||
clean: mostlyclean
|
||||
- del ctags.exe
|
||||
@@ -0,0 +1,104 @@
|
||||
# $Id: mk_os2.mak,v 1.3 2002/01/27 21:20:55 darren Exp $
|
||||
#
|
||||
# A Makefile for OS/2 using EMX/gcc
|
||||
# You may want to use the OS/2 port of autoconf for building
|
||||
# and comment-out the according statements in this Makefile.
|
||||
# You need a library to provide regex support.
|
||||
# libExt might do this, but currently (2/2001) it doesn't work well
|
||||
# together with ctags ...
|
||||
#
|
||||
# Provided and supported by
|
||||
# Alexander Mai
|
||||
# <[email protected]> or <[email protected]>
|
||||
|
||||
default:
|
||||
@echo "Enter $(MAKE) -f mk_os2.mak target"
|
||||
@echo "where target is one of:"
|
||||
@echo " small (small executable req. EMX runtime)"
|
||||
@echo " debug (executable for debugging purposes)"
|
||||
@echo " release (stand-alone executable)"
|
||||
@echo " clean (remove all files built)"
|
||||
|
||||
# Use this to create a small binary
|
||||
# (requires EMX runtime libraries)
|
||||
small:
|
||||
$(MAKE) -f mk_os2.mak all \
|
||||
CC="gcc" \
|
||||
CFLAGS="-O5 -mpentium -Wall" \
|
||||
LFLAGS="-Zcrtdll -s" \
|
||||
LIBS="-lregex" \
|
||||
OBJEXT="o"
|
||||
|
||||
# Use this to create a binary for debugging purposes
|
||||
# (requires EMX runtime libraries)
|
||||
debug:
|
||||
$(MAKE) -f mk_os2.mak all \
|
||||
CC="gcc" \
|
||||
CFLAGS="-O0 -Wall -g" \
|
||||
LFLAGS="-Zcrtdll -g" \
|
||||
LIBS="-lregex" \
|
||||
OBJEXT="o"
|
||||
|
||||
# Use this to create a stand-alone binary for distribution
|
||||
# (requires link386 for linking but no EMX runtime libraries)
|
||||
release:
|
||||
$(MAKE) -f mk_os2.mak all \
|
||||
CC="gcc" \
|
||||
CFLAGS="-g -O5 -mpentium -Wall" \
|
||||
LFLAGS="-s -Zomf -Zsys -Zlinker /PM:VIO" \
|
||||
LIBS="-lregex" \
|
||||
OBJEXT="obj"
|
||||
|
||||
# Use the line below if you have created config.h
|
||||
# (manually or by running configure)
|
||||
# Otherwise use built-in defaults (#ifdef OS2)!
|
||||
# DEFINES=-DHAVE_CONFIG_H
|
||||
DEFINES=-DOS2
|
||||
|
||||
|
||||
# General rules and definitions
|
||||
|
||||
.SUFFIXES: .c .exe .h .o .obj
|
||||
|
||||
include source.mak
|
||||
|
||||
all: ctags.exe readtags.exe readtags.a readtags.lib
|
||||
ctags: ctags.exe
|
||||
etags: etags.exe
|
||||
|
||||
ctags.exe: $(OBJECTS)
|
||||
$(CC) $(CFLAGS) $(LFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
etags.exe: ctags.exe
|
||||
@copy $< $@
|
||||
|
||||
# The readtags executable
|
||||
readtags.exe: readtags.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(LFLAGS) -DREADTAGS_MAIN -o $@ $^ $(LIBS)
|
||||
|
||||
# We build a.out and omf version of the readtags library
|
||||
readtags.o: readtags.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $^
|
||||
|
||||
readtags.a: readtags.o
|
||||
if exist $@ del $@
|
||||
ar rc $@ $^
|
||||
|
||||
readtags.lib: readtags.a
|
||||
emxomf $<
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(DEFINES) -I. -c $< -o $@
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) -Zomf $(DEFINES) -I. -c $< -o $@
|
||||
|
||||
# Delete all files that are not part of the source distribution
|
||||
clean:
|
||||
@if exist ctags.exe del ctags.exe
|
||||
@if exist etags.exe del etags.exe
|
||||
@if exist readtags.exe del readtags.exe
|
||||
@if exist readtags.a del readtags.a
|
||||
@if exist readtags.lib del readtags.lib
|
||||
@if exist *.obj del *.obj
|
||||
@if exist *.o del *.o
|
||||
@@ -0,0 +1,100 @@
|
||||
# $Id: mk_qdos.mak,v 1.10 2003/02/13 02:59:30 darren Exp $
|
||||
#
|
||||
# Makefile for ctags on QDOS/SMS systems and C68 v4.24
|
||||
# Submitted by Thierry Godefroy <[email protected]>
|
||||
|
||||
# Directories:
|
||||
|
||||
T = ram1_
|
||||
P = drv1_C68_
|
||||
|
||||
# Programs name:
|
||||
|
||||
CC = $(P)cc
|
||||
AS = $(P)as68
|
||||
ASM = $(P)qmac
|
||||
LD = $(P)ld
|
||||
|
||||
# Programs flags:
|
||||
|
||||
CCFLAGS = -tmp$(T) -v -Y$(P) -I$(P)include_ -O
|
||||
ASFLAGS = -V
|
||||
ASMFLAGS = -nolist
|
||||
LDFLAGS = -v -L$(P)lib_ -bufp150K\
|
||||
|
||||
# Target name:
|
||||
|
||||
EXEC = ctags
|
||||
|
||||
# Additional libraries:
|
||||
|
||||
LIBS =
|
||||
|
||||
# Target dependencies:
|
||||
|
||||
OBJEXT = o
|
||||
|
||||
HEADERS = e_qdos.h \
|
||||
args.h ctags.h debug.h entry.h general.h get.h keyword.h \
|
||||
main.h options.h parse.h parsers.h read.h routines.h sort.h \
|
||||
strlist.h vstring.h
|
||||
|
||||
OBJECTS = qdos.$(OBJEXT) \
|
||||
args.$(OBJEXT) \
|
||||
asm.$(OBJEXT) \
|
||||
asp.$(OBJEXT) \
|
||||
awk.$(OBJEXT) \
|
||||
beta.$(OBJEXT) \
|
||||
c.$(OBJEXT) \
|
||||
cobol.$(OBJEXT) \
|
||||
eiffel.$(OBJEXT) \
|
||||
entry.$(OBJEXT) \
|
||||
erlang.$(OBJEXT) \
|
||||
fortran.$(OBJEXT) \
|
||||
get.$(OBJEXT) \
|
||||
keyword.$(OBJEXT) \
|
||||
lisp.$(OBJEXT) \
|
||||
lregex.$(OBJEXT) \
|
||||
lua.$(OBJEXT) \
|
||||
main.$(OBJEXT) \
|
||||
make.$(OBJEXT) \
|
||||
options.$(OBJEXT) \
|
||||
parse.$(OBJEXT) \
|
||||
pascal.$(OBJEXT) \
|
||||
perl.$(OBJEXT) \
|
||||
php.$(OBJEXT) \
|
||||
python.$(OBJEXT) \
|
||||
read.$(OBJEXT) \
|
||||
rexx.$(OBJEXT) \
|
||||
routines.$(OBJEXT) \
|
||||
ruby.$(OBJEXT) \
|
||||
scheme.$(OBJEXT) \
|
||||
sh.$(OBJEXT) \
|
||||
slang.$(OBJEXT) \
|
||||
sort.$(OBJEXT) \
|
||||
sml.$(OBJEXT) \
|
||||
sql.$(OBJEXT) \
|
||||
strlist.$(OBJEXT) \
|
||||
tcl.$(OBJEXT) \
|
||||
verilog.$(OBJEXT) \
|
||||
vim.$(OBJEXT) \
|
||||
vstring.$(OBJEXT) \
|
||||
yacc.$(OBJEXT)
|
||||
|
||||
$(EXEC) : $(OBJECTS)
|
||||
$(LD) -o$(EXEC) $(LDFLAGS) $(OBJECTS) $(LIBS)
|
||||
|
||||
$(OBJECTS): $(HEADERS)
|
||||
|
||||
# Construction rules:
|
||||
|
||||
_c_o :
|
||||
$(CC) -c $(CCFLAGS) $<
|
||||
|
||||
_s_o :
|
||||
$(AS) $(ASFLAGS) $< $@
|
||||
|
||||
_asm_rel :
|
||||
$(ASM) $< $(ASMFLAGS)
|
||||
|
||||
#end
|
||||
@@ -0,0 +1,63 @@
|
||||
# $Id: mk_sas.mak,v 1.9 2003/02/13 02:59:30 darren Exp $
|
||||
#
|
||||
# Makefile for SAS/C Amiga Compiler
|
||||
# Submitted by Stefan Haubenthal <[email protected]>
|
||||
|
||||
CFLAGS= def AMIGA opt parm r sint
|
||||
|
||||
OBJEXT = o
|
||||
|
||||
OBJECTS = \
|
||||
args.$(OBJEXT) \
|
||||
asm.$(OBJEXT) \
|
||||
asp.$(OBJEXT) \
|
||||
awk.$(OBJEXT) \
|
||||
beta.$(OBJEXT) \
|
||||
c.$(OBJEXT) \
|
||||
cobol.$(OBJEXT) \
|
||||
eiffel.$(OBJEXT) \
|
||||
entry.$(OBJEXT) \
|
||||
erlang.$(OBJEXT) \
|
||||
fortran.$(OBJEXT) \
|
||||
get.$(OBJEXT) \
|
||||
keyword.$(OBJEXT) \
|
||||
lisp.$(OBJEXT) \
|
||||
lregex.$(OBJEXT) \
|
||||
lua.$(OBJEXT) \
|
||||
main.$(OBJEXT) \
|
||||
make.$(OBJEXT) \
|
||||
options.$(OBJEXT) \
|
||||
parse.$(OBJEXT) \
|
||||
pascal.$(OBJEXT) \
|
||||
perl.$(OBJEXT) \
|
||||
php.$(OBJEXT) \
|
||||
python.$(OBJEXT) \
|
||||
read.$(OBJEXT) \
|
||||
rexx.$(OBJEXT) \
|
||||
routines.$(OBJEXT) \
|
||||
ruby.$(OBJEXT) \
|
||||
scheme.$(OBJEXT) \
|
||||
sh.$(OBJEXT) \
|
||||
slang.$(OBJEXT) \
|
||||
sort.$(OBJEXT) \
|
||||
sml.$(OBJEXT) \
|
||||
sql.$(OBJEXT) \
|
||||
strlist.$(OBJEXT) \
|
||||
tcl.$(OBJEXT) \
|
||||
verilog.$(OBJEXT) \
|
||||
vim.$(OBJEXT) \
|
||||
vstring.$(OBJEXT) \
|
||||
yacc.$(OBJEXT)
|
||||
|
||||
ctags: $(OBJECTS)
|
||||
sc link to $@ $(OBJECTS) math s sint
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -o $*.o $*.c
|
||||
|
||||
clean:
|
||||
-delete $(OBJECTS) ctags.lnk
|
||||
|
||||
archive: clean
|
||||
@-delete force RAM:ctags.lha
|
||||
lha -r a RAM:ctags // ctags
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#! /bin/sh
|
||||
# mkinstalldirs --- make directory hierarchy
|
||||
# Author: Noah Friedman <[email protected]>
|
||||
# Created: 1993-05-16
|
||||
# Public domain
|
||||
|
||||
# $Id: mkinstalldirs,v 1.1 2001/11/02 04:53:43 darren Exp $
|
||||
|
||||
errstatus=0
|
||||
|
||||
for file
|
||||
do
|
||||
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
|
||||
shift
|
||||
|
||||
pathcomp=
|
||||
for d
|
||||
do
|
||||
pathcomp="$pathcomp$d"
|
||||
case "$pathcomp" in
|
||||
-* ) pathcomp=./$pathcomp ;;
|
||||
esac
|
||||
|
||||
if test ! -d "$pathcomp"; then
|
||||
echo "mkdir $pathcomp" 1>&2
|
||||
|
||||
mkdir "$pathcomp" || lasterr=$?
|
||||
|
||||
if test ! -d "$pathcomp"; then
|
||||
errstatus=$lasterr
|
||||
fi
|
||||
fi
|
||||
|
||||
pathcomp="$pathcomp/"
|
||||
done
|
||||
done
|
||||
|
||||
exit $errstatus
|
||||
|
||||
# mkinstalldirs ends here
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* $Id: options.h,v 1.12 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Defines external interface to option processing.
|
||||
*/
|
||||
#ifndef _OPTIONS_H
|
||||
#define _OPTIONS_H
|
||||
|
||||
#if defined(OPTION_WRITE) || defined(VAXC)
|
||||
# define CONST_OPTION
|
||||
#else
|
||||
# define CONST_OPTION const
|
||||
#endif
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "args.h"
|
||||
#include "parse.h"
|
||||
#include "strlist.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
typedef enum { OPTION_NONE, OPTION_SHORT, OPTION_LONG } optionType;
|
||||
|
||||
typedef struct sCookedArgs {
|
||||
/* private */
|
||||
Arguments* args;
|
||||
char *shortOptions;
|
||||
char simple[2];
|
||||
boolean isOption;
|
||||
boolean longOption;
|
||||
const char* parameter;
|
||||
/* public */
|
||||
char* item;
|
||||
} cookedArgs;
|
||||
|
||||
typedef enum eLocate {
|
||||
EX_MIX, /* line numbers for defines, patterns otherwise */
|
||||
EX_LINENUM, /* -n only line numbers in tag file */
|
||||
EX_PATTERN /* -N only patterns in tag file */
|
||||
} exCmd;
|
||||
|
||||
typedef enum sortType {
|
||||
SO_UNSORTED,
|
||||
SO_SORTED,
|
||||
SO_FOLDSORTED
|
||||
} sortType;
|
||||
|
||||
struct sInclude {
|
||||
boolean fileNames; /* include tags for source file names */
|
||||
boolean qualifiedTags; /* include tags for qualified class members */
|
||||
boolean fileScope; /* include tags of file scope only */
|
||||
};
|
||||
|
||||
struct sExtFields { /* extension field content control */
|
||||
boolean access;
|
||||
boolean fileScope;
|
||||
boolean implementation;
|
||||
boolean inheritance;
|
||||
boolean kind;
|
||||
boolean kindKey;
|
||||
boolean kindLong;
|
||||
boolean language;
|
||||
boolean lineNumber;
|
||||
boolean scope;
|
||||
boolean signature;
|
||||
boolean typeRef;
|
||||
};
|
||||
|
||||
/* This stores the command line options.
|
||||
*/
|
||||
typedef struct sOptionValues {
|
||||
struct sInclude include;/* --extra extra tag inclusion */
|
||||
struct sExtFields extensionFields;/* --fields extension field control */
|
||||
stringList* ignore; /* -I name of file containing tokens to ignore */
|
||||
boolean append; /* -a append to "tags" file */
|
||||
boolean backward; /* -B regexp patterns search backwards */
|
||||
boolean etags; /* -e output Emacs style tags file */
|
||||
exCmd locate; /* --excmd EX command used to locate tag */
|
||||
boolean recurse; /* -R recurse into directories */
|
||||
sortType sorted; /* -u,--sort sort tags */
|
||||
boolean verbose; /* -V verbose */
|
||||
boolean xref; /* -x generate xref output instead */
|
||||
char *fileList; /* -L name of file containing names of files */
|
||||
char *tagFileName; /* -o name of tags file */
|
||||
stringList* headerExt; /* -h header extensions */
|
||||
stringList* etagsInclude;/* --etags-include list of TAGS files to include*/
|
||||
unsigned int tagFileFormat;/* --format tag file format (level) */
|
||||
boolean if0; /* --if0 examine code within "#if 0" branch */
|
||||
boolean kindLong; /* --kind-long */
|
||||
langType language; /* --lang specified language override */
|
||||
boolean followLinks; /* --link follow symbolic links? */
|
||||
boolean filter; /* --filter behave as filter: files in, tags out */
|
||||
char* filterTerminator; /* --filter-terminator string to output */
|
||||
boolean tagRelative; /* --tag-relative file paths relative to tag file */
|
||||
boolean printTotals; /* --totals print cumulative statistics */
|
||||
boolean lineDirectives; /* --linedirectives process #line directives */
|
||||
#ifdef DEBUG
|
||||
long debugLevel; /* -D debugging output */
|
||||
unsigned long breakLine;/* -b source line at which to call lineBreak() */
|
||||
#endif
|
||||
} optionValues;
|
||||
|
||||
/*
|
||||
* GLOBAL VARIABLES
|
||||
*/
|
||||
extern CONST_OPTION optionValues Option;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void verbose (const char *const format, ...) __printf__ (1, 2);
|
||||
extern void freeList (stringList** const pString);
|
||||
extern void setDefaultTagFileName (void);
|
||||
extern void checkOptions (void);
|
||||
extern boolean filesRequired (void);
|
||||
extern void testEtagsInvocation (void);
|
||||
|
||||
extern cookedArgs* cArgNewFromString (const char* string);
|
||||
extern cookedArgs* cArgNewFromArgv (char* const* const argv);
|
||||
extern cookedArgs* cArgNewFromFile (FILE* const fp);
|
||||
extern cookedArgs* cArgNewFromLineFile (FILE* const fp);
|
||||
extern void cArgDelete (cookedArgs* const current);
|
||||
extern boolean cArgOff (cookedArgs* const current);
|
||||
extern boolean cArgIsOption (cookedArgs* const current);
|
||||
extern const char* cArgItem (cookedArgs* const current);
|
||||
extern void cArgForth (cookedArgs* const current);
|
||||
|
||||
extern boolean isExcludedFile (const char* const name);
|
||||
extern boolean isIncludeFile (const char *const fileName);
|
||||
extern boolean isIgnoreToken (const char *const name, boolean *const pIgnoreParens, const char **const replacement);
|
||||
extern void parseOption (cookedArgs* const cargs);
|
||||
extern void parseOptions (cookedArgs* const cargs);
|
||||
extern void previewFirstOption (cookedArgs* const cargs);
|
||||
extern void readOptionConfiguration (void);
|
||||
extern void initOptions (void);
|
||||
extern void freeOptionResources (void);
|
||||
|
||||
#endif /* _OPTIONS_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,678 @@
|
||||
/*
|
||||
* $Id: parse.c,v 1.16 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for managing source languages and
|
||||
* dispatching files to the appropriate language parser.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
#ifdef HAVE_FNMATCH_H
|
||||
#include <fnmatch.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "entry.h"
|
||||
#include "main.h"
|
||||
#define OPTION_WRITE
|
||||
#include "options.h"
|
||||
#include "parsers.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static parserDefinitionFunc* BuiltInParsers[] = { PARSER_LIST };
|
||||
static parserDefinition** LanguageTable = NULL;
|
||||
static unsigned int LanguageCount = 0;
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern void makeSimpleTag (
|
||||
const vString* const name, kindOption* const kinds, const int kind)
|
||||
{
|
||||
if (kinds [kind].enabled && name != NULL && vStringLength (name) > 0)
|
||||
{
|
||||
tagEntryInfo e;
|
||||
initTagEntry (&e, vStringValue (name));
|
||||
|
||||
e.kindName = kinds [kind].name;
|
||||
e.kind = kinds [kind].letter;
|
||||
|
||||
makeTagEntry (&e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* parserDescription mapping management
|
||||
*/
|
||||
|
||||
extern parserDefinition* parserNew (const char* name)
|
||||
{
|
||||
parserDefinition* result = xCalloc (1, parserDefinition);
|
||||
result->name = eStrdup (name);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern const char *getLanguageName (const langType language)
|
||||
{
|
||||
const char* result;
|
||||
if (language == LANG_IGNORE)
|
||||
result = "unknown";
|
||||
else
|
||||
{
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
result = LanguageTable [language]->name;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern langType getNamedLanguage (const char *const name)
|
||||
{
|
||||
langType result = LANG_IGNORE;
|
||||
unsigned int i;
|
||||
Assert (name != NULL);
|
||||
for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
|
||||
{
|
||||
const parserDefinition* const lang = LanguageTable [i];
|
||||
if (lang->name != NULL)
|
||||
if (strcasecmp (name, lang->name) == 0)
|
||||
result = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static langType getExtensionLanguage (const char *const extension)
|
||||
{
|
||||
langType result = LANG_IGNORE;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
|
||||
{
|
||||
stringList* const exts = LanguageTable [i]->currentExtensions;
|
||||
if (exts != NULL && stringListExtensionMatched (exts, extension))
|
||||
result = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static langType getPatternLanguage (const char *const fileName)
|
||||
{
|
||||
langType result = LANG_IGNORE;
|
||||
const char* base = baseFilename (fileName);
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
|
||||
{
|
||||
stringList* const ptrns = LanguageTable [i]->currentPatterns;
|
||||
if (ptrns != NULL && stringListFileMatched (ptrns, base))
|
||||
result = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef SYS_INTERPRETER
|
||||
|
||||
/* The name of the language interpreter, either directly or as the argument
|
||||
* to "env".
|
||||
*/
|
||||
static vString* determineInterpreter (const char* const cmd)
|
||||
{
|
||||
vString* const interpreter = vStringNew ();
|
||||
const char* p = cmd;
|
||||
do
|
||||
{
|
||||
vStringClear (interpreter);
|
||||
for ( ; isspace ((int) *p) ; ++p)
|
||||
; /* no-op */
|
||||
for ( ; *p != '\0' && ! isspace ((int) *p) ; ++p)
|
||||
vStringPut (interpreter, (int) *p);
|
||||
vStringTerminate (interpreter);
|
||||
} while (strcmp (vStringValue (interpreter), "env") == 0);
|
||||
return interpreter;
|
||||
}
|
||||
|
||||
static langType getInterpreterLanguage (const char *const fileName)
|
||||
{
|
||||
langType result = LANG_IGNORE;
|
||||
FILE* const fp = fopen (fileName, "r");
|
||||
if (fp != NULL)
|
||||
{
|
||||
vString* const vLine = vStringNew ();
|
||||
const char* const line = readLine (vLine, fp);
|
||||
if (line != NULL && line [0] == '#' && line [1] == '!')
|
||||
{
|
||||
const char* const lastSlash = strrchr (line, '/');
|
||||
const char *const cmd = lastSlash != NULL ? lastSlash+1 : line+2;
|
||||
vString* const interpreter = determineInterpreter (cmd);
|
||||
result = getExtensionLanguage (vStringValue (interpreter));
|
||||
vStringDelete (interpreter);
|
||||
}
|
||||
vStringDelete (vLine);
|
||||
fclose (fp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern langType getFileLanguage (const char *const fileName)
|
||||
{
|
||||
langType language = Option.language;
|
||||
if (language == LANG_AUTO)
|
||||
{
|
||||
language = getExtensionLanguage (fileExtension (fileName));
|
||||
if (language == LANG_IGNORE)
|
||||
language = getPatternLanguage (fileName);
|
||||
#ifdef SYS_INTERPRETER
|
||||
if (language == LANG_IGNORE)
|
||||
{
|
||||
fileStatus *status = eStat (fileName);
|
||||
if (status->isExecutable)
|
||||
language = getInterpreterLanguage (fileName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
extern void printLanguageMap (const langType language)
|
||||
{
|
||||
boolean first = TRUE;
|
||||
unsigned int i;
|
||||
stringList* map = LanguageTable [language]->currentPatterns;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
|
||||
{
|
||||
printf ("%s(%s)", (first ? "" : " "),
|
||||
vStringValue (stringListItem (map, i)));
|
||||
first = FALSE;
|
||||
}
|
||||
map = LanguageTable [language]->currentExtensions;
|
||||
for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
|
||||
{
|
||||
printf ("%s.%s", (first ? "" : " "),
|
||||
vStringValue (stringListItem (map, i)));
|
||||
first = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
extern void installLanguageMapDefault (const langType language)
|
||||
{
|
||||
parserDefinition* lang;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
if (lang->currentPatterns != NULL)
|
||||
stringListDelete (lang->currentPatterns);
|
||||
if (lang->currentExtensions != NULL)
|
||||
stringListDelete (lang->currentExtensions);
|
||||
|
||||
if (lang->patterns == NULL)
|
||||
lang->currentPatterns = stringListNew ();
|
||||
else
|
||||
{
|
||||
lang->currentPatterns =
|
||||
stringListNewFromArgv (lang->patterns);
|
||||
}
|
||||
if (lang->extensions == NULL)
|
||||
lang->currentExtensions = stringListNew ();
|
||||
else
|
||||
{
|
||||
lang->currentExtensions =
|
||||
stringListNewFromArgv (lang->extensions);
|
||||
}
|
||||
if (Option.verbose)
|
||||
printLanguageMap (language);
|
||||
verbose ("\n");
|
||||
}
|
||||
|
||||
extern void installLanguageMapDefaults (void)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
{
|
||||
verbose (" %s: ", getLanguageName (i));
|
||||
installLanguageMapDefault (i);
|
||||
}
|
||||
}
|
||||
|
||||
extern void clearLanguageMap (const langType language)
|
||||
{
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
stringListClear (LanguageTable [language]->currentPatterns);
|
||||
stringListClear (LanguageTable [language]->currentExtensions);
|
||||
}
|
||||
|
||||
extern void addLanguagePatternMap (const langType language, const char* ptrn)
|
||||
{
|
||||
vString* const str = vStringNewInit (ptrn);
|
||||
parserDefinition* lang;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
if (lang->currentPatterns == NULL)
|
||||
lang->currentPatterns = stringListNew ();
|
||||
stringListAdd (lang->currentPatterns, str);
|
||||
}
|
||||
|
||||
extern boolean removeLanguageExtensionMap (const char *const extension)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount && ! result ; ++i)
|
||||
{
|
||||
stringList* const exts = LanguageTable [i]->currentExtensions;
|
||||
if (exts != NULL && stringListRemoveExtension (exts, extension))
|
||||
{
|
||||
verbose (" (removed from %s)", getLanguageName (i));
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void addLanguageExtensionMap (
|
||||
const langType language, const char* extension)
|
||||
{
|
||||
vString* const str = vStringNewInit (extension);
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
removeLanguageExtensionMap (extension);
|
||||
stringListAdd (LanguageTable [language]->currentExtensions, str);
|
||||
}
|
||||
|
||||
extern void enableLanguage (const langType language, const boolean state)
|
||||
{
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
LanguageTable [language]->enabled = state;
|
||||
}
|
||||
|
||||
extern void enableLanguages (const boolean state)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
enableLanguage (i, state);
|
||||
}
|
||||
|
||||
static void initializeParsers (void)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
if (LanguageTable [i]->initialize != NULL)
|
||||
(LanguageTable [i]->initialize) ((langType) i);
|
||||
}
|
||||
|
||||
extern void initializeParsing (void)
|
||||
{
|
||||
unsigned int builtInCount;
|
||||
unsigned int i;
|
||||
|
||||
builtInCount = sizeof (BuiltInParsers) / sizeof (BuiltInParsers [0]);
|
||||
LanguageTable = xMalloc (builtInCount, parserDefinition*);
|
||||
|
||||
verbose ("Installing parsers: ");
|
||||
for (i = 0 ; i < builtInCount ; ++i)
|
||||
{
|
||||
parserDefinition* const def = (*BuiltInParsers [i]) ();
|
||||
if (def != NULL)
|
||||
{
|
||||
boolean accepted = FALSE;
|
||||
if (def->name == NULL || def->name[0] == '\0')
|
||||
error (FATAL, "parser definition must contain name\n");
|
||||
else if (def->regex)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
def->parser = findRegexTags;
|
||||
accepted = TRUE;
|
||||
#endif
|
||||
}
|
||||
else if ((def->parser == NULL) == (def->parser2 == NULL))
|
||||
error (FATAL,
|
||||
"%s parser definition must define one and only one parsing routine\n",
|
||||
def->name);
|
||||
else
|
||||
accepted = TRUE;
|
||||
if (accepted)
|
||||
{
|
||||
verbose ("%s%s", i > 0 ? ", " : "", def->name);
|
||||
def->id = LanguageCount++;
|
||||
LanguageTable [def->id] = def;
|
||||
}
|
||||
}
|
||||
}
|
||||
verbose ("\n");
|
||||
enableLanguages (TRUE);
|
||||
initializeParsers ();
|
||||
}
|
||||
|
||||
extern void freeParserResources (void)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
{
|
||||
parserDefinition* const lang = LanguageTable [i];
|
||||
freeList (&lang->currentPatterns);
|
||||
freeList (&lang->currentExtensions);
|
||||
eFree (lang->name);
|
||||
lang->name = NULL;
|
||||
eFree (lang);
|
||||
}
|
||||
if (LanguageTable != NULL)
|
||||
eFree (LanguageTable);
|
||||
LanguageTable = NULL;
|
||||
LanguageCount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Option parsing
|
||||
*/
|
||||
|
||||
extern void processLanguageDefineOption (
|
||||
const char *const option, const char *const parameter __unused__)
|
||||
{
|
||||
#ifdef HAVE_REGEX
|
||||
if (parameter [0] == '\0')
|
||||
error (WARNING, "No language specified for \"%s\" option", option);
|
||||
else if (getNamedLanguage (parameter) != LANG_IGNORE)
|
||||
error (WARNING, "Language \"%s\" already defined", parameter);
|
||||
else
|
||||
{
|
||||
unsigned int i = LanguageCount++;
|
||||
parserDefinition* const def = parserNew (parameter);
|
||||
def->parser = findRegexTags;
|
||||
def->currentPatterns = stringListNew ();
|
||||
def->currentExtensions = stringListNew ();
|
||||
def->regex = TRUE;
|
||||
def->enabled = TRUE;
|
||||
def->id = i;
|
||||
LanguageTable = xRealloc (LanguageTable, i + 1, parserDefinition*);
|
||||
LanguageTable [i] = def;
|
||||
}
|
||||
#else
|
||||
error (WARNING, "regex support not available; required for --%s option",
|
||||
option);
|
||||
#endif
|
||||
}
|
||||
|
||||
static kindOption *langKindOption (const langType language, const int flag)
|
||||
{
|
||||
unsigned int i;
|
||||
kindOption* result = NULL;
|
||||
const parserDefinition* lang;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
for (i=0 ; i < lang->kindCount && result == NULL ; ++i)
|
||||
if (lang->kinds [i].letter == flag)
|
||||
result = &lang->kinds [i];
|
||||
return result;
|
||||
}
|
||||
|
||||
static void disableLanguageKinds (const langType language)
|
||||
{
|
||||
const parserDefinition* lang;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
if (lang->regex)
|
||||
disableRegexKinds (language);
|
||||
else
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < lang->kindCount ; ++i)
|
||||
lang->kinds [i].enabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean enableLanguageKind (
|
||||
const langType language, const int kind, const boolean mode)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
if (LanguageTable [language]->regex)
|
||||
result = enableRegexKind (language, kind, mode);
|
||||
else
|
||||
{
|
||||
kindOption* const opt = langKindOption (language, kind);
|
||||
if (opt != NULL)
|
||||
{
|
||||
opt->enabled = mode;
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void processLangKindOption (
|
||||
const langType language, const char *const option,
|
||||
const char *const parameter)
|
||||
{
|
||||
const char *p = parameter;
|
||||
boolean mode = TRUE;
|
||||
int c;
|
||||
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
if (*p != '+' && *p != '-')
|
||||
disableLanguageKinds (language);
|
||||
while ((c = *p++) != '\0') switch (c)
|
||||
{
|
||||
case '+': mode = TRUE; break;
|
||||
case '-': mode = FALSE; break;
|
||||
default:
|
||||
if (! enableLanguageKind (language, c, mode))
|
||||
error (WARNING, "Unsupported parameter '%c' for --%s option",
|
||||
c, option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern boolean processKindOption (
|
||||
const char *const option, const char *const parameter)
|
||||
{
|
||||
boolean handled = FALSE;
|
||||
const char* const dash = strchr (option, '-');
|
||||
if (dash != NULL &&
|
||||
(strcmp (dash + 1, "kinds") == 0 || strcmp (dash + 1, "types") == 0))
|
||||
{
|
||||
langType language;
|
||||
vString* langName = vStringNew ();
|
||||
vStringNCopyS (langName, option, dash - option);
|
||||
language = getNamedLanguage (vStringValue (langName));
|
||||
if (language == LANG_IGNORE)
|
||||
error (WARNING, "Unknown language specified in \"%s\" option", option);
|
||||
else
|
||||
processLangKindOption (language, option, parameter);
|
||||
vStringDelete (langName);
|
||||
handled = TRUE;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
static void printLanguageKind (const kindOption* const kind, boolean indent)
|
||||
{
|
||||
const char *const indentation = indent ? " " : "";
|
||||
printf ("%s%c %s%s\n", indentation, kind->letter,
|
||||
kind->description != NULL ? kind->description :
|
||||
(kind->name != NULL ? kind->name : ""),
|
||||
kind->enabled ? "" : " [off]");
|
||||
}
|
||||
|
||||
static void printKinds (langType language, boolean indent)
|
||||
{
|
||||
const parserDefinition* lang;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
if (lang->kinds != NULL || lang->regex)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < lang->kindCount ; ++i)
|
||||
printLanguageKind (lang->kinds + i, indent);
|
||||
printRegexKinds (language, indent);
|
||||
}
|
||||
}
|
||||
|
||||
extern void printLanguageKinds (const langType language)
|
||||
{
|
||||
if (language == LANG_AUTO)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
{
|
||||
const parserDefinition* const lang = LanguageTable [i];
|
||||
printf ("%s%s\n", lang->name, lang->enabled ? "" : " [disabled]");
|
||||
printKinds (i, TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
printKinds (language, FALSE);
|
||||
}
|
||||
|
||||
static void printMaps (const langType language)
|
||||
{
|
||||
const parserDefinition* lang;
|
||||
unsigned int i;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
printf ("%-8s", lang->name);
|
||||
if (lang->currentExtensions != NULL)
|
||||
for (i = 0 ; i < stringListCount (lang->currentExtensions) ; ++i)
|
||||
printf (" *.%s", vStringValue (
|
||||
stringListItem (lang->currentExtensions, i)));
|
||||
if (lang->currentPatterns != NULL)
|
||||
for (i = 0 ; i < stringListCount (lang->currentPatterns) ; ++i)
|
||||
printf (" %s", vStringValue (
|
||||
stringListItem (lang->currentPatterns, i)));
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
extern void printLanguageMaps (const langType language)
|
||||
{
|
||||
if (language == LANG_AUTO)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
printMaps (i);
|
||||
}
|
||||
else
|
||||
printMaps (language);
|
||||
}
|
||||
|
||||
static void printLanguage (const langType language)
|
||||
{
|
||||
const parserDefinition* lang;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
lang = LanguageTable [language];
|
||||
if (lang->kinds != NULL || lang->regex)
|
||||
printf ("%s%s\n", lang->name, lang->enabled ? "" : " [disabled]");
|
||||
}
|
||||
|
||||
extern void printLanguageList (void)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < LanguageCount ; ++i)
|
||||
printLanguage (i);
|
||||
}
|
||||
|
||||
/*
|
||||
* File parsing
|
||||
*/
|
||||
|
||||
static void makeFileTag (const char *const fileName)
|
||||
{
|
||||
if (Option.include.fileNames)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, baseFilename (fileName));
|
||||
|
||||
tag.isFileEntry = TRUE;
|
||||
tag.lineNumberEntry = TRUE;
|
||||
tag.lineNumber = 1;
|
||||
tag.kindName = "file";
|
||||
tag.kind = 'F';
|
||||
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean createTagsForFile (
|
||||
const char *const fileName, const langType language,
|
||||
const unsigned int passCount)
|
||||
{
|
||||
boolean retried = FALSE;
|
||||
Assert (0 <= language && language < (int) LanguageCount);
|
||||
if (fileOpen (fileName, language))
|
||||
{
|
||||
const parserDefinition* const lang = LanguageTable [language];
|
||||
if (Option.etags)
|
||||
beginEtagsFile ();
|
||||
|
||||
makeFileTag (fileName);
|
||||
|
||||
if (lang->parser != NULL)
|
||||
lang->parser ();
|
||||
else if (lang->parser2 != NULL)
|
||||
retried = lang->parser2 (passCount);
|
||||
|
||||
if (Option.etags)
|
||||
endEtagsFile (getSourceFileTagPath ());
|
||||
|
||||
fileClose ();
|
||||
}
|
||||
|
||||
return retried;
|
||||
}
|
||||
|
||||
static boolean createTagsWithFallback (
|
||||
const char *const fileName, const langType language)
|
||||
{
|
||||
const unsigned long numTags = TagFile.numTags.added;
|
||||
fpos_t tagFilePosition;
|
||||
unsigned int passCount = 0;
|
||||
boolean tagFileResized = FALSE;
|
||||
|
||||
fgetpos (TagFile.fp, &tagFilePosition);
|
||||
while (createTagsForFile (fileName, language, ++passCount))
|
||||
{
|
||||
/* Restore prior state of tag file.
|
||||
*/
|
||||
fsetpos (TagFile.fp, &tagFilePosition);
|
||||
TagFile.numTags.added = numTags;
|
||||
tagFileResized = TRUE;
|
||||
}
|
||||
return tagFileResized;
|
||||
}
|
||||
|
||||
extern boolean parseFile (const char *const fileName)
|
||||
{
|
||||
boolean tagFileResized = FALSE;
|
||||
langType language = Option.language;
|
||||
if (Option.language == LANG_AUTO)
|
||||
language = getFileLanguage (fileName);
|
||||
Assert (language != LANG_AUTO);
|
||||
if (language == LANG_IGNORE)
|
||||
verbose ("ignoring %s (unknown language)\n", fileName);
|
||||
else if (! LanguageTable [language]->enabled)
|
||||
verbose ("ignoring %s (language disabled)\n", fileName);
|
||||
else
|
||||
{
|
||||
if (Option.filter)
|
||||
openTagFile ();
|
||||
|
||||
tagFileResized = createTagsWithFallback (fileName, language);
|
||||
|
||||
if (Option.filter)
|
||||
closeTagFile (tagFileResized);
|
||||
addTotals (1, 0L, 0L);
|
||||
|
||||
return tagFileResized;
|
||||
}
|
||||
return tagFileResized;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4 nowrap: */
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* $Id: parse.h,v 1.16 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Private definitions for parsing support.
|
||||
*/
|
||||
#ifndef _PARSE_H
|
||||
#define _PARSE_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
#include "parsers.h" /* contains list of parsers */
|
||||
#include "strlist.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define KIND_COUNT(kindTable) (sizeof(kindTable)/sizeof(kindOption))
|
||||
|
||||
#define LANG_AUTO (-1)
|
||||
#define LANG_IGNORE (-2)
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef int langType;
|
||||
|
||||
typedef void (*createRegexTag) (const vString* const name);
|
||||
typedef void (*simpleParser) (void);
|
||||
typedef boolean (*rescanParser) (const unsigned int passCount);
|
||||
typedef void (*parserInitialize) (langType language);
|
||||
|
||||
typedef struct sKindOption {
|
||||
boolean enabled; /* are tags for kind enabled? */
|
||||
int letter; /* kind letter */
|
||||
const char* name; /* kind name */
|
||||
const char* description; /* displayed in --help output */
|
||||
} kindOption;
|
||||
|
||||
typedef struct {
|
||||
/* defined by parser */
|
||||
char* name; /* name of language */
|
||||
kindOption* kinds; /* tag kinds handled by parser */
|
||||
unsigned int kindCount; /* size of `kinds' list */
|
||||
const char *const *extensions; /* list of default extensions */
|
||||
const char *const *patterns; /* list of default file name patterns */
|
||||
parserInitialize initialize; /* initialization routine, if needed */
|
||||
simpleParser parser; /* simple parser (common case) */
|
||||
rescanParser parser2; /* rescanning parser (unusual case) */
|
||||
boolean regex; /* is this a regex parser? */
|
||||
|
||||
/* used internally */
|
||||
unsigned int id; /* id assigned to language */
|
||||
boolean enabled; /* currently enabled? */
|
||||
stringList* currentPatterns; /* current list of file name patterns */
|
||||
stringList* currentExtensions; /* current list of extensions */
|
||||
} parserDefinition;
|
||||
|
||||
typedef parserDefinition* (parserDefinitionFunc) (void);
|
||||
|
||||
typedef struct {
|
||||
size_t start; /* character index in line where match starts */
|
||||
size_t length; /* length of match */
|
||||
} regexMatch;
|
||||
|
||||
typedef void (*regexCallback) (const char *line, const regexMatch *matches, unsigned int count);
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
|
||||
/* Each parsers' definition function is called. The routine is expected to
|
||||
* return a structure allocated using parserNew(). This structure must,
|
||||
* at minimum, set the `parser' field.
|
||||
*/
|
||||
extern parserDefinitionFunc PARSER_LIST;
|
||||
|
||||
/* Legacy interface */
|
||||
extern boolean includingDefineTags (void);
|
||||
|
||||
/* Language processing and parsing */
|
||||
extern void makeSimpleTag (const vString* const name, kindOption* const kinds, const int kind);
|
||||
extern parserDefinition* parserNew (const char* name);
|
||||
extern const char *getLanguageName (const langType language);
|
||||
extern langType getNamedLanguage (const char *const name);
|
||||
extern langType getFileLanguage (const char *const fileName);
|
||||
extern void installLanguageMapDefault (const langType language);
|
||||
extern void installLanguageMapDefaults (void);
|
||||
extern void clearLanguageMap (const langType language);
|
||||
extern boolean removeLanguageExtensionMap (const char *const extension);
|
||||
extern void addLanguageExtensionMap (const langType language, const char* extension);
|
||||
extern void addLanguagePatternMap (const langType language, const char* ptrn);
|
||||
extern void printLanguageMap (const langType language);
|
||||
extern void printLanguageMaps (const langType language);
|
||||
extern void enableLanguages (const boolean state);
|
||||
extern void enableLanguage (const langType language, const boolean state);
|
||||
extern void initializeParsing (void);
|
||||
extern void freeParserResources (void);
|
||||
extern void processLanguageDefineOption (const char *const option, const char *const parameter);
|
||||
extern boolean processKindOption (const char *const option, const char *const parameter);
|
||||
extern void printKindOptions (void);
|
||||
extern void printLanguageKinds (const langType language);
|
||||
extern void printLanguageList (void);
|
||||
extern boolean parseFile (const char *const fileName);
|
||||
|
||||
/* Regex interface */
|
||||
#ifdef HAVE_REGEX
|
||||
extern void findRegexTags (void);
|
||||
extern boolean matchRegex (const vString* const line, const langType language);
|
||||
#endif
|
||||
extern boolean processRegexOption (const char *const option, const char *const parameter);
|
||||
extern void addLanguageRegex (const langType language, const char* const regex);
|
||||
extern void addTagRegex (const langType language, const char* const regex, const char* const name, const char* const kinds, const char* const flags);
|
||||
extern void addCallbackRegex (const langType language, const char *const regex, const char *const flags, const regexCallback callback);
|
||||
extern void disableRegexKinds (const langType language);
|
||||
extern boolean enableRegexKind (const langType language, const int kind, const boolean mode);
|
||||
extern void printRegexKinds (const langType language, boolean indent);
|
||||
extern void freeRegexResources (void);
|
||||
extern void checkRegex (void);
|
||||
|
||||
#endif /* _PARSE_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* $Id: parsers.h,v 1.12 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to all language parsing modules.
|
||||
*
|
||||
* To add a new language parser, you need only modify this single source
|
||||
* file to add the name of the parser definition function.
|
||||
*/
|
||||
#ifndef _PARSERS_H
|
||||
#define _PARSERS_H
|
||||
|
||||
/* Add the name of any new parser definition function here */
|
||||
#define PARSER_LIST \
|
||||
AsmParser, \
|
||||
AspParser, \
|
||||
AwkParser, \
|
||||
BetaParser, \
|
||||
CParser, \
|
||||
CppParser, \
|
||||
CsharpParser, \
|
||||
CobolParser, \
|
||||
EiffelParser, \
|
||||
ErlangParser, \
|
||||
FortranParser, \
|
||||
HtmlParser, \
|
||||
JavaParser, \
|
||||
JavaScriptParser, \
|
||||
LispParser, \
|
||||
LuaParser, \
|
||||
MakefileParser, \
|
||||
PascalParser, \
|
||||
PerlParser, \
|
||||
PhpParser, \
|
||||
PythonParser, \
|
||||
RexxParser, \
|
||||
RubyParser, \
|
||||
SchemeParser, \
|
||||
ShParser, \
|
||||
SlangParser, \
|
||||
SmlParser, \
|
||||
SqlParser, \
|
||||
TclParser, \
|
||||
VeraParser, \
|
||||
VerilogParser, \
|
||||
VimParser, \
|
||||
YaccParser
|
||||
|
||||
#endif /* _PARSERS_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* $Id: pascal.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2001-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for the Pascal language,
|
||||
* including some extensions for Object Pascal.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "entry.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_FUNCTION, K_PROCEDURE
|
||||
} pascalKind;
|
||||
|
||||
static kindOption PascalKinds [] = {
|
||||
{ TRUE, 'f', "function", "functions"},
|
||||
{ TRUE, 'p', "procedure", "procedures"}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void createPascalTag (
|
||||
tagEntryInfo* const tag, const vString* const name, const int kind)
|
||||
{
|
||||
if (PascalKinds [kind].enabled && name != NULL && vStringLength (name) > 0)
|
||||
{
|
||||
initTagEntry (tag, vStringValue (name));
|
||||
tag->kindName = PascalKinds [kind].name;
|
||||
tag->kind = PascalKinds [kind].letter;
|
||||
}
|
||||
else
|
||||
initTagEntry (tag, NULL);
|
||||
}
|
||||
|
||||
static void makePascalTag (const tagEntryInfo* const tag)
|
||||
{
|
||||
if (tag->name != NULL)
|
||||
makeTagEntry (tag);
|
||||
}
|
||||
|
||||
static const unsigned char* dbp;
|
||||
|
||||
#define starttoken(c) (isalpha ((int) c) || (int) c == '_')
|
||||
#define intoken(c) (isalnum ((int) c) || (int) c == '_' || (int) c == '.')
|
||||
#define endtoken(c) (! intoken (c) && ! isdigit ((int) c))
|
||||
|
||||
static boolean tail (const char *cp)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
register int len = 0;
|
||||
|
||||
while (*cp != '\0' && tolower ((int) *cp) == tolower ((int) dbp [len]))
|
||||
cp++, len++;
|
||||
if (*cp == '\0' && !intoken (dbp [len]))
|
||||
{
|
||||
dbp += len;
|
||||
result = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Algorithm adapted from from GNU etags.
|
||||
* Locates tags for procedures & functions. Doesn't do any type- or
|
||||
* var-definitions. It does look for the keyword "extern" or "forward"
|
||||
* immediately following the procedure statement; if found, the tag is
|
||||
* skipped.
|
||||
*/
|
||||
static void findPascalTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
tagEntryInfo tag;
|
||||
pascalKind kind = K_FUNCTION;
|
||||
/* each of these flags is TRUE iff: */
|
||||
boolean incomment = FALSE; /* point is inside a comment */
|
||||
int comment_char = '\0'; /* type of current comment */
|
||||
boolean inquote = FALSE; /* point is inside '..' string */
|
||||
boolean get_tagname = FALSE;/* point is after PROCEDURE/FUNCTION
|
||||
keyword, so next item = potential tag */
|
||||
boolean found_tag = FALSE; /* point is after a potential tag */
|
||||
boolean inparms = FALSE; /* point is within parameter-list */
|
||||
boolean verify_tag = FALSE;
|
||||
/* point has passed the parm-list, so the next token will determine
|
||||
* whether this is a FORWARD/EXTERN to be ignored, or whether it is a
|
||||
* real tag
|
||||
*/
|
||||
|
||||
dbp = fileReadLine ();
|
||||
while (dbp != NULL)
|
||||
{
|
||||
int c = *dbp++;
|
||||
|
||||
if (c == '\0') /* if end of line */
|
||||
{
|
||||
dbp = fileReadLine ();
|
||||
if (dbp == NULL || *dbp == '\0')
|
||||
continue;
|
||||
if (!((found_tag && verify_tag) || get_tagname))
|
||||
c = *dbp++;
|
||||
/* only if don't need *dbp pointing to the beginning of
|
||||
* the name of the procedure or function
|
||||
*/
|
||||
}
|
||||
if (incomment)
|
||||
{
|
||||
if (comment_char == '{' && c == '}')
|
||||
incomment = FALSE;
|
||||
else if (comment_char == '(' && c == '*' && *dbp == ')')
|
||||
{
|
||||
dbp++;
|
||||
incomment = FALSE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (inquote)
|
||||
{
|
||||
if (c == '\'')
|
||||
inquote = FALSE;
|
||||
continue;
|
||||
}
|
||||
else switch (c)
|
||||
{
|
||||
case '\'':
|
||||
inquote = TRUE; /* found first quote */
|
||||
continue;
|
||||
case '{': /* found open { comment */
|
||||
incomment = TRUE;
|
||||
comment_char = c;
|
||||
continue;
|
||||
case '(':
|
||||
if (*dbp == '*') /* found open (* comment */
|
||||
{
|
||||
incomment = TRUE;
|
||||
comment_char = c;
|
||||
dbp++;
|
||||
}
|
||||
else if (found_tag) /* found '(' after tag, i.e., parm-list */
|
||||
inparms = TRUE;
|
||||
continue;
|
||||
case ')': /* end of parms list */
|
||||
if (inparms)
|
||||
inparms = FALSE;
|
||||
continue;
|
||||
case ';':
|
||||
if (found_tag && !inparms) /* end of proc or fn stmt */
|
||||
{
|
||||
verify_tag = TRUE;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (found_tag && verify_tag && *dbp != ' ')
|
||||
{
|
||||
/* check if this is an "extern" declaration */
|
||||
if (*dbp == '\0')
|
||||
continue;
|
||||
if (tolower ((int) *dbp == 'e'))
|
||||
{
|
||||
if (tail ("extern")) /* superfluous, really! */
|
||||
{
|
||||
found_tag = FALSE;
|
||||
verify_tag = FALSE;
|
||||
}
|
||||
}
|
||||
else if (tolower ((int) *dbp) == 'f')
|
||||
{
|
||||
if (tail ("forward")) /* check for forward reference */
|
||||
{
|
||||
found_tag = FALSE;
|
||||
verify_tag = FALSE;
|
||||
}
|
||||
}
|
||||
if (found_tag && verify_tag) /* not external proc, so make tag */
|
||||
{
|
||||
found_tag = FALSE;
|
||||
verify_tag = FALSE;
|
||||
makePascalTag (&tag);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (get_tagname) /* grab name of proc or fn */
|
||||
{
|
||||
const unsigned char *cp;
|
||||
|
||||
if (*dbp == '\0')
|
||||
continue;
|
||||
|
||||
/* grab block name */
|
||||
while (isspace ((int) *dbp))
|
||||
++dbp;
|
||||
for (cp = dbp ; *cp != '\0' && !endtoken (*cp) ; cp++)
|
||||
continue;
|
||||
vStringNCopyS (name, (const char*) dbp, cp - dbp);
|
||||
createPascalTag (&tag, name, kind);
|
||||
dbp = cp; /* set dbp to e-o-token */
|
||||
get_tagname = FALSE;
|
||||
found_tag = TRUE;
|
||||
/* and proceed to check for "extern" */
|
||||
}
|
||||
else if (!incomment && !inquote && !found_tag)
|
||||
{
|
||||
switch (tolower ((int) c))
|
||||
{
|
||||
case 'c':
|
||||
if (tail ("onstructor"))
|
||||
{
|
||||
get_tagname = TRUE;
|
||||
kind = K_PROCEDURE;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (tail ("estructor"))
|
||||
{
|
||||
get_tagname = TRUE;
|
||||
kind = K_PROCEDURE;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (tail ("rocedure"))
|
||||
{
|
||||
get_tagname = TRUE;
|
||||
kind = K_PROCEDURE;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (tail ("unction"))
|
||||
{
|
||||
get_tagname = TRUE;
|
||||
kind = K_FUNCTION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} /* while not eof */
|
||||
}
|
||||
}
|
||||
|
||||
extern parserDefinition* PascalParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "p", "pas", NULL };
|
||||
parserDefinition* def = parserNew ("Pascal");
|
||||
def->extensions = extensions;
|
||||
def->kinds = PascalKinds;
|
||||
def->kindCount = KIND_COUNT (PascalKinds);
|
||||
def->parser = findPascalTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* $Id: perl.c,v 1.14 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for PERL language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_NONE = -1,
|
||||
K_CONSTANT,
|
||||
K_LABEL,
|
||||
K_SUBROUTINE
|
||||
} perlKind;
|
||||
|
||||
static kindOption PerlKinds [] = {
|
||||
{ TRUE, 'c', "constant", "constants" },
|
||||
{ TRUE, 'l', "label", "labels" },
|
||||
{ TRUE, 's', "subroutine", "subroutines" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static boolean isIdentifier1 (int c)
|
||||
{
|
||||
return (boolean) (isalpha (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isIdentifier (int c)
|
||||
{
|
||||
return (boolean) (isalnum (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isPodWord (const char *word)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
if (isalpha (*word))
|
||||
{
|
||||
const char *const pods [] = {
|
||||
"head1", "head2", "head3", "head4", "over", "item", "back",
|
||||
"pod", "begin", "end", "for"
|
||||
};
|
||||
const size_t count = sizeof (pods) / sizeof (pods [0]);
|
||||
const char *white = strpbrk (word, " \t");
|
||||
const size_t len = (white!=NULL) ? (size_t)(white-word) : strlen (word);
|
||||
char *const id = (char*) eMalloc (len + 1);
|
||||
size_t i;
|
||||
strncpy (id, word, len);
|
||||
id [len] = '\0';
|
||||
for (i = 0 ; i < count && ! result ; ++i)
|
||||
{
|
||||
if (strcmp (id, pods [i]) == 0)
|
||||
result = TRUE;
|
||||
}
|
||||
eFree (id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Algorithm adapted from from GNU etags.
|
||||
* Perl support by Bart Robinson <lomew@cs.utah.edu>
|
||||
* Perl sub names: look for /^ [ \t\n]sub [ \t\n]+ [^ \t\n{ (]+/
|
||||
*/
|
||||
static void findPerlTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
vString *package = NULL;
|
||||
boolean skipPodDoc = FALSE;
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
boolean spaceRequired = FALSE;
|
||||
boolean qualified = FALSE;
|
||||
const unsigned char *cp = line;
|
||||
perlKind kind = K_NONE;
|
||||
|
||||
if (skipPodDoc)
|
||||
{
|
||||
if (strncmp ((const char*) line, "=cut", (size_t) 4) == 0)
|
||||
skipPodDoc = FALSE;
|
||||
continue;
|
||||
}
|
||||
else if (line [0] == '=')
|
||||
{
|
||||
skipPodDoc = isPodWord ((const char*)line + 1);
|
||||
continue;
|
||||
}
|
||||
else if (strcmp ((const char*) line, "__DATA__") == 0)
|
||||
break;
|
||||
else if (strcmp ((const char*) line, "__END__") == 0)
|
||||
break;
|
||||
else if (line [0] == '#')
|
||||
continue;
|
||||
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
|
||||
if (strncmp((const char*) cp, "sub", (size_t) 3) == 0)
|
||||
{
|
||||
cp += 3;
|
||||
kind = K_SUBROUTINE;
|
||||
spaceRequired = TRUE;
|
||||
qualified = TRUE;
|
||||
}
|
||||
else if (strncmp((const char*) cp, "use", (size_t) 3) == 0)
|
||||
{
|
||||
cp += 3;
|
||||
if (!isspace(*cp))
|
||||
continue;
|
||||
while (*cp && isspace (*cp))
|
||||
++cp;
|
||||
if (strncmp((const char*) cp, "constant", (size_t) 8) != 0)
|
||||
continue;
|
||||
cp += 8;
|
||||
kind = K_CONSTANT;
|
||||
spaceRequired = TRUE;
|
||||
qualified = TRUE;
|
||||
}
|
||||
else if (strncmp((const char*) cp, "package", (size_t) 7) == 0)
|
||||
{
|
||||
cp += 7;
|
||||
if (package == NULL)
|
||||
package = vStringNew ();
|
||||
else
|
||||
vStringClear (package);
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
while ((int) *cp != ';' && !isspace ((int) *cp))
|
||||
{
|
||||
vStringPut (package, (int) *cp);
|
||||
cp++;
|
||||
}
|
||||
vStringCatS (package, "::");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isIdentifier1 (*cp))
|
||||
{
|
||||
const unsigned char *p = cp;
|
||||
while (isIdentifier (*p))
|
||||
++p;
|
||||
if ((int) *p == ':')
|
||||
kind = K_LABEL;
|
||||
}
|
||||
}
|
||||
if (kind != K_NONE)
|
||||
{
|
||||
if (spaceRequired && !isspace (*cp))
|
||||
continue;
|
||||
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
while (isIdentifier (*cp))
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
cp++;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
if (vStringLength (name) > 0)
|
||||
{
|
||||
makeSimpleTag (name, PerlKinds, kind);
|
||||
if (Option.include.qualifiedTags && qualified &&
|
||||
package != NULL && vStringLength (package) > 0)
|
||||
{
|
||||
vString *const qualifiedName = vStringNew ();
|
||||
vStringCopy (qualifiedName, package);
|
||||
vStringCat (qualifiedName, name);
|
||||
makeSimpleTag (qualifiedName, PerlKinds, kind);
|
||||
vStringDelete (qualifiedName);
|
||||
}
|
||||
}
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
if (package != NULL)
|
||||
vStringDelete (package);
|
||||
}
|
||||
|
||||
extern parserDefinition* PerlParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "pl", "pm", "plx", "perl", NULL };
|
||||
parserDefinition* def = parserNew ("Perl");
|
||||
def->kinds = PerlKinds;
|
||||
def->kindCount = KIND_COUNT (PerlKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findPerlTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* $Id: php.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000, Jesus Castagnetto <jmcastagnetto@zkey.com>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for the PHP web page
|
||||
* scripting language. Only recognizes functions and classes, not methods or
|
||||
* variables.
|
||||
*
|
||||
* Parsing PHP defines by Pavel Hlousek <pavel.hlousek@seznam.cz>, Apr 2003.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CLASS, K_DEFINE, K_FUNCTION, K_VARIABLE
|
||||
} phpKind;
|
||||
|
||||
static kindOption PhpKinds [] = {
|
||||
{ TRUE, 'c', "class", "classes" },
|
||||
{ TRUE, 'd', "define", "constant definitions" },
|
||||
{ TRUE, 'f', "function", "functions" },
|
||||
{ TRUE, 'v', "variable", "variables" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static boolean isLetter(const int c)
|
||||
{
|
||||
return (boolean)(isalpha(c) || (c >= 127 && c <= 255));
|
||||
}
|
||||
|
||||
static boolean isVarChar1(const int c)
|
||||
{
|
||||
return (boolean)(isLetter (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isVarChar(const int c)
|
||||
{
|
||||
return (boolean)(isVarChar1 (c) || isdigit (c));
|
||||
}
|
||||
|
||||
static void findPhpTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
|
||||
if (*(const char*)cp == '$' && isVarChar1 (*(const char*)(cp+1)))
|
||||
{
|
||||
cp += 1;
|
||||
vStringClear (name);
|
||||
while (isVarChar ((int) *cp))
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (*(const char*) cp == '=')
|
||||
{
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, PhpKinds, K_VARIABLE);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
else if (strncmp ((const char*) cp, "function", (size_t) 8) == 0 &&
|
||||
isspace ((int) cp [8]))
|
||||
{
|
||||
cp += 8;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (*cp == '&') /* skip reference character */
|
||||
cp++;
|
||||
|
||||
vStringClear (name);
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, PhpKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0 &&
|
||||
isspace ((int) cp [5]))
|
||||
{
|
||||
cp += 5;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
vStringClear (name);
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, PhpKinds, K_CLASS);
|
||||
vStringClear (name);
|
||||
}
|
||||
else if (strncmp ((const char*) cp, "define", (size_t) 6) == 0 &&
|
||||
! isalnum ((int) cp [6]))
|
||||
{
|
||||
cp += 6;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (*cp != '(')
|
||||
continue;
|
||||
++cp;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if ((*cp == '\'') || (*cp == '"'))
|
||||
++cp;
|
||||
else if (! ((*cp == '_') || isalnum ((int) *cp)))
|
||||
continue;
|
||||
|
||||
vStringClear (name);
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, PhpKinds, K_DEFINE);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* PhpParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "php", "php3", "phtml", NULL };
|
||||
parserDefinition* def = parserNew ("PHP");
|
||||
def->kinds = PhpKinds;
|
||||
def->kindCount = KIND_COUNT (PhpKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findPhpTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* $Id: python.c,v 1.9 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for Python language
|
||||
* files.
|
||||
*/
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "entry.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CLASS, K_FUNCTION, K_MEMBER
|
||||
} pythonKind;
|
||||
|
||||
static kindOption PythonKinds[] = {
|
||||
{TRUE, 'c', "class", "classes"},
|
||||
{TRUE, 'f', "function", "functions"},
|
||||
{TRUE, 'm', "member", "class members"}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
/* tagEntryInfo and vString should be preinitialized/preallocated but not
|
||||
* necessary. If successful you will find class name in vString
|
||||
*/
|
||||
|
||||
static boolean isIdentifierFirstCharacter (int c)
|
||||
{
|
||||
return (boolean) (isalpha (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isIdentifierCharacter (int c)
|
||||
{
|
||||
return (boolean) (isalnum (c) || c == '_');
|
||||
}
|
||||
|
||||
static void makeFunctionTag (vString *const function, vString *const class)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (function));
|
||||
if (vStringLength (class) > 0)
|
||||
{
|
||||
tag.kindName = "member";
|
||||
tag.kind = 'm';
|
||||
tag.extensionFields.scope [0] = "class";
|
||||
tag.extensionFields.scope [1] = vStringValue (class);
|
||||
}
|
||||
else
|
||||
{
|
||||
tag.kindName = "function";
|
||||
tag.kind = 'f';
|
||||
}
|
||||
if (strncmp (vStringValue (function), "__", 2) == 0 &&
|
||||
strcmp (vStringValue (function), "__init__") != 0)
|
||||
{
|
||||
tag.extensionFields.access = "private";
|
||||
tag.isFileScope = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
tag.extensionFields.access = "public";
|
||||
}
|
||||
makeTagEntry (&tag);
|
||||
if (vStringLength (class) > 0 && Option.include.qualifiedTags)
|
||||
{
|
||||
vString *tagname = vStringNew ();
|
||||
vStringCat (tagname, class);
|
||||
vStringPut (tagname, '.');
|
||||
vStringCat (tagname, function);
|
||||
tag.name = vStringValue (tagname);
|
||||
makeTagEntry (&tag);
|
||||
vStringDelete (tagname);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeClassTag (vString *const class, vString *const inheritance)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (class));
|
||||
tag.kindName = "class";
|
||||
tag.kind = 'c';
|
||||
tag.extensionFields.inheritance = vStringValue (inheritance);
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
|
||||
static const unsigned char *skipSpace (const unsigned char *cp)
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
static const unsigned char *parseIdentifier (
|
||||
const unsigned char *cp, vString *const identifier)
|
||||
{
|
||||
vStringClear (identifier);
|
||||
while (isIdentifierCharacter ((int) *cp))
|
||||
{
|
||||
vStringPut (identifier, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (identifier);
|
||||
return cp;
|
||||
}
|
||||
|
||||
static void parseClass (const unsigned char *cp, vString *const class)
|
||||
{
|
||||
vString *const inheritance = vStringNew ();
|
||||
vStringClear (inheritance);
|
||||
cp = parseIdentifier (cp, class);
|
||||
cp = skipSpace (cp);
|
||||
if (*cp == '(')
|
||||
{
|
||||
++cp;
|
||||
while (*cp != ')' && *cp != '\0')
|
||||
{
|
||||
vStringPut (inheritance, *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (inheritance);
|
||||
}
|
||||
makeClassTag (class, inheritance);
|
||||
vStringDelete (inheritance);
|
||||
}
|
||||
|
||||
static void parseFunction (const unsigned char *cp, vString *const class)
|
||||
{
|
||||
vString *const identifier = vStringNew ();
|
||||
cp = parseIdentifier (cp, identifier);
|
||||
makeFunctionTag (identifier, class);
|
||||
vStringDelete (identifier);
|
||||
}
|
||||
|
||||
static void findPythonTags (void)
|
||||
{
|
||||
vString *const class = vStringNew ();
|
||||
vString *const identifier = vStringNew ();
|
||||
const unsigned char *line;
|
||||
int class_indent = 0;
|
||||
boolean longStringLiteral = FALSE;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
int indent;
|
||||
|
||||
cp = skipSpace (cp);
|
||||
indent = cp - line;
|
||||
|
||||
if (*cp == '#' || *cp == '\0') /* skip comment or blank line */
|
||||
continue;
|
||||
|
||||
if (longStringLiteral)
|
||||
{
|
||||
cp = (const unsigned char*) strstr ((const char*) cp, "\"\"\"");
|
||||
if (cp == NULL)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
longStringLiteral = FALSE;
|
||||
cp += 3;
|
||||
}
|
||||
}
|
||||
if (isIdentifierFirstCharacter ((int) *cp))
|
||||
{
|
||||
if (indent <= class_indent)
|
||||
vStringClear (class);
|
||||
|
||||
cp = parseIdentifier (cp, identifier);
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
cp = skipSpace (cp);
|
||||
if (strcmp (vStringValue (identifier), "def") == 0)
|
||||
parseFunction (cp, class);
|
||||
else if (strcmp (vStringValue (identifier), "class") == 0)
|
||||
{
|
||||
parseClass (cp, class);
|
||||
class_indent = indent;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((cp = (const unsigned char*) strstr ((const char*)cp, "\"\"\"")) != NULL)
|
||||
{
|
||||
cp += 3;
|
||||
cp = (const unsigned char*) strstr ((const char*) cp, "\"\"\"");
|
||||
if (cp == NULL)
|
||||
longStringLiteral = TRUE;
|
||||
}
|
||||
}
|
||||
vStringDelete (identifier);
|
||||
vStringDelete (class);
|
||||
}
|
||||
|
||||
extern parserDefinition *PythonParser (void)
|
||||
{
|
||||
static const char *const extensions[] = { "py", "python", NULL };
|
||||
parserDefinition *def = parserNew ("Python");
|
||||
def->kinds = PythonKinds;
|
||||
def->kindCount = KIND_COUNT (PythonKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findPythonTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* $Id: qdos.c,v 1.2 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1999, Thierry Godefroy <godefroy@imaginet.fr>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions to handle wildcard expansion and file name
|
||||
* conversion under QDOS.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <qdos.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "ctags.h"
|
||||
|
||||
/* Translate the filenames from UNIX to QDOS conventions on open calls */
|
||||
int (*_Open) (const char *, int, ...) = qopen;
|
||||
|
||||
long _stack = 24576; /* Plenty of stack space */
|
||||
long _memincr = 10240; /* Big increments to cut fragmentation */
|
||||
char _prog_name [] = "ctags";
|
||||
char _version [] = PROGRAM_VERSION;
|
||||
char _copyright [32] = __DATE__;
|
||||
char *_endmsg = "\nPress a key to exit.";
|
||||
int custom_expand (char * param, char ***argvptr, int *argcptr);
|
||||
int (*_cmdwildcard) () = custom_expand;
|
||||
|
||||
|
||||
struct WINDOWDEF _condetails = { 208, 1, 0, 7, 512, 256, 0, 0};
|
||||
void (*_consetup) () = consetup_title;
|
||||
|
||||
/* custom cmdexpand: also expands directory names */
|
||||
|
||||
#define FILEBUF_INIT 1024 /* Initial allocation size for buffer */
|
||||
#define FILEBUF_INCR 1024 /* Increment size for buffer */
|
||||
|
||||
int custom_expand (char * param, char ***argvptr, int *argcptr)
|
||||
{
|
||||
int count,sl;
|
||||
size_t bufsize;
|
||||
char *filenamebuf;
|
||||
char *ptr,*safeptr;
|
||||
|
||||
/*
|
||||
* Check to see if we should do wild card expansion.
|
||||
* We only perform wildcard expansion if the parameter
|
||||
* was not a string and if it contains one of the
|
||||
* wild card characters.
|
||||
*
|
||||
* We also do not expand any option that starts with '-'
|
||||
* as we then assume that it is a unix stylew option.
|
||||
*/
|
||||
if ((*param == '-') || (strpbrk (param,"*?") == NULL) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((filenamebuf = malloc (bufsize = FILEBUF_INIT)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
TRYAGAIN:
|
||||
count = getfnl (param, filenamebuf, bufsize, QDR_ALL);
|
||||
if (count == -1 && errno == ENOMEM) {
|
||||
/*
|
||||
* We have overflowed the buffer, so we try
|
||||
* to get a bigger buffer and try again.
|
||||
*/
|
||||
bufsize += FILEBUF_INCR;
|
||||
if ((filenamebuf = realloc (filenamebuf, bufsize)) == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
goto TRYAGAIN;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If no files were found, then return unexpanded.
|
||||
*/
|
||||
if (count == 0) {
|
||||
free (filenamebuf);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Files were found, so add these to the list instead
|
||||
* of the original parameter typed by the user.
|
||||
*/
|
||||
for ( ptr=filenamebuf ; count > 0 ; count -- ) {
|
||||
*argvptr = (char **) realloc (*argvptr, (size_t) (((*argcptr) + 2) * sizeof (char *)));
|
||||
safeptr= (char *) malloc ((size_t) (sl=strlen (ptr) + 1));
|
||||
if (safeptr == NULL || *argvptr == NULL) {
|
||||
return -1;
|
||||
}
|
||||
(void) memcpy (safeptr,ptr, (size_t) sl);
|
||||
(*argvptr) [*argcptr] = safeptr;
|
||||
*argcptr += 1;
|
||||
ptr += sl;
|
||||
}
|
||||
free (filenamebuf);
|
||||
return *argcptr;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
* $Id: read.c,v 1.7 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains low level source and tag file read functions (newline
|
||||
* conversion for source files are performed at this level).
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define FILE_WRITE
|
||||
#include "read.h"
|
||||
#include "debug.h"
|
||||
#include "entry.h"
|
||||
#include "main.h"
|
||||
#include "routines.h"
|
||||
#include "options.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
inputFile File; /* globally read through macros */
|
||||
static fpos_t StartOfLine; /* holds deferred position of start of line */
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern void freeSourceFileResources (void)
|
||||
{
|
||||
vStringDelete (File.name);
|
||||
vStringDelete (File.path);
|
||||
vStringDelete (File.source.name);
|
||||
vStringDelete (File.line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Source file access functions
|
||||
*/
|
||||
|
||||
static void setInputFileName (const char *const fileName)
|
||||
{
|
||||
const char *const head = fileName;
|
||||
const char *const tail = baseFilename (head);
|
||||
|
||||
if (File.name != NULL)
|
||||
vStringDelete (File.name);
|
||||
File.name = vStringNewInit (fileName);
|
||||
|
||||
if (File.path != NULL)
|
||||
vStringDelete (File.path);
|
||||
if (tail == head)
|
||||
File.path = NULL;
|
||||
else
|
||||
{
|
||||
const size_t length = tail - head - 1;
|
||||
File.path = vStringNew ();
|
||||
vStringNCopyS (File.path, fileName, length);
|
||||
}
|
||||
}
|
||||
|
||||
static void setSourceFileParameters (vString *const fileName)
|
||||
{
|
||||
if (File.source.name != NULL)
|
||||
vStringDelete (File.source.name);
|
||||
File.source.name = fileName;
|
||||
|
||||
if (File.source.tagPath != NULL)
|
||||
eFree (File.source.tagPath);
|
||||
if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName)))
|
||||
File.source.tagPath = eStrdup (vStringValue (fileName));
|
||||
else
|
||||
File.source.tagPath =
|
||||
relativeFilename (vStringValue (fileName), TagFile.directory);
|
||||
|
||||
if (vStringLength (fileName) > TagFile.max.file)
|
||||
TagFile.max.file = vStringLength (fileName);
|
||||
|
||||
File.source.isHeader = isIncludeFile (vStringValue (fileName));
|
||||
File.source.language = getFileLanguage (vStringValue (fileName));
|
||||
}
|
||||
|
||||
static boolean setSourceFileName (vString *const fileName)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE)
|
||||
{
|
||||
vString *pathName;
|
||||
if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
|
||||
pathName = vStringNewCopy (fileName);
|
||||
else
|
||||
pathName = combinePathAndFile (
|
||||
vStringValue (File.path), vStringValue (fileName));
|
||||
setSourceFileParameters (pathName);
|
||||
result = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Line directive parsing
|
||||
*/
|
||||
|
||||
static int skipWhite (void)
|
||||
{
|
||||
int c;
|
||||
do
|
||||
c = getc (File.fp);
|
||||
while (c == ' ' || c == '\t');
|
||||
return c;
|
||||
}
|
||||
|
||||
static unsigned long readLineNumber (void)
|
||||
{
|
||||
unsigned long lNum = 0;
|
||||
int c = skipWhite ();
|
||||
while (c != EOF && isdigit (c))
|
||||
{
|
||||
lNum = (lNum * 10) + (c - '0');
|
||||
c = getc (File.fp);
|
||||
}
|
||||
ungetc (c, File.fp);
|
||||
if (c != ' ' && c != '\t')
|
||||
lNum = 0;
|
||||
|
||||
return lNum;
|
||||
}
|
||||
|
||||
/* While ANSI only permits lines of the form:
|
||||
* # line n "filename"
|
||||
* Earlier compilers generated lines of the form
|
||||
* # n filename
|
||||
* GNU C will output lines of the form:
|
||||
* # n "filename"
|
||||
* So we need to be fairly flexible in what we accept.
|
||||
*/
|
||||
static vString *readFileName (void)
|
||||
{
|
||||
vString *const fileName = vStringNew ();
|
||||
boolean quoteDelimited = FALSE;
|
||||
int c = skipWhite ();
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
c = getc (File.fp); /* skip double-quote */
|
||||
quoteDelimited = TRUE;
|
||||
}
|
||||
while (c != EOF && c != '\n' &&
|
||||
(quoteDelimited ? (c != '"') : (c != ' ' && c != '\t')))
|
||||
{
|
||||
vStringPut (fileName, c);
|
||||
c = getc (File.fp);
|
||||
}
|
||||
if (c == '\n')
|
||||
ungetc (c, File.fp);
|
||||
vStringPut (fileName, '\0');
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
static boolean parseLineDirective (void)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
int c = skipWhite ();
|
||||
DebugStatement ( const char* lineStr = ""; )
|
||||
|
||||
if (isdigit (c))
|
||||
{
|
||||
ungetc (c, File.fp);
|
||||
result = TRUE;
|
||||
}
|
||||
else if (c == 'l' && getc (File.fp) == 'i' &&
|
||||
getc (File.fp) == 'n' && getc (File.fp) == 'e')
|
||||
{
|
||||
c = getc (File.fp);
|
||||
if (c == ' ' || c == '\t')
|
||||
{
|
||||
DebugStatement ( lineStr = "line"; )
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
const unsigned long lNum = readLineNumber ();
|
||||
if (lNum == 0)
|
||||
result = FALSE;
|
||||
else
|
||||
{
|
||||
vString *const fileName = readFileName ();
|
||||
if (vStringLength (fileName) == 0)
|
||||
{
|
||||
File.source.lineNumber = lNum - 1; /* applies to NEXT line */
|
||||
DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); )
|
||||
}
|
||||
else if (setSourceFileName (fileName))
|
||||
{
|
||||
File.source.lineNumber = lNum - 1; /* applies to NEXT line */
|
||||
DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"",
|
||||
lineStr, lNum, vStringValue (fileName)); )
|
||||
}
|
||||
|
||||
if (Option.include.fileNames && vStringLength (fileName) > 0 &&
|
||||
lNum == 1)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, baseFilename (vStringValue (fileName)));
|
||||
|
||||
tag.isFileEntry = TRUE;
|
||||
tag.lineNumberEntry = TRUE;
|
||||
tag.lineNumber = 1;
|
||||
tag.kindName = "file";
|
||||
tag.kind = 'F';
|
||||
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
vStringDelete (fileName);
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Source file I/O operations
|
||||
*/
|
||||
|
||||
/* This function opens a source file, and resets the line counter. If it
|
||||
* fails, it will display an error message and leave the File.fp set to NULL.
|
||||
*/
|
||||
extern boolean fileOpen (const char *const fileName, const langType language)
|
||||
{
|
||||
#ifdef VMS
|
||||
const char *const openMode = "r";
|
||||
#else
|
||||
const char *const openMode = "rb";
|
||||
#endif
|
||||
boolean opened = FALSE;
|
||||
|
||||
/* If another file was already open, then close it.
|
||||
*/
|
||||
if (File.fp != NULL)
|
||||
{
|
||||
fclose (File.fp); /* close any open source file */
|
||||
File.fp = NULL;
|
||||
}
|
||||
|
||||
File.fp = fopen (fileName, openMode);
|
||||
if (File.fp == NULL)
|
||||
error (WARNING | PERROR, "cannot open \"%s\"", fileName);
|
||||
else
|
||||
{
|
||||
opened = TRUE;
|
||||
|
||||
setInputFileName (fileName);
|
||||
fgetpos (File.fp, &StartOfLine);
|
||||
fgetpos (File.fp, &File.filePosition);
|
||||
File.currentLine = NULL;
|
||||
File.language = language;
|
||||
File.lineNumber = 0L;
|
||||
File.eof = FALSE;
|
||||
File.newLine = TRUE;
|
||||
|
||||
if (File.line != NULL)
|
||||
vStringClear (File.line);
|
||||
|
||||
setSourceFileParameters (vStringNewInit (fileName));
|
||||
File.source.lineNumber = 0L;
|
||||
|
||||
verbose ("OPENING %s as %s language %sfile\n", fileName,
|
||||
getLanguageName (language),
|
||||
File.source.isHeader ? "include " : "");
|
||||
}
|
||||
return opened;
|
||||
}
|
||||
|
||||
extern void fileClose (void)
|
||||
{
|
||||
if (File.fp != NULL)
|
||||
{
|
||||
/* The line count of the file is 1 too big, since it is one-based
|
||||
* and is incremented upon each newline.
|
||||
*/
|
||||
if (Option.printTotals)
|
||||
{
|
||||
fileStatus *status = eStat (vStringValue (File.name));
|
||||
addTotals (0, File.lineNumber - 1L, status->size);
|
||||
}
|
||||
fclose (File.fp);
|
||||
File.fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
extern boolean fileEOF (void)
|
||||
{
|
||||
return File.eof;
|
||||
}
|
||||
|
||||
/* Action to take for each encountered source newline.
|
||||
*/
|
||||
static void fileNewline (void)
|
||||
{
|
||||
File.filePosition = StartOfLine;
|
||||
File.newLine = FALSE;
|
||||
File.lineNumber++;
|
||||
File.source.lineNumber++;
|
||||
DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); )
|
||||
DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )
|
||||
}
|
||||
|
||||
/* This function reads a single character from the stream, performing newline
|
||||
* canonicalization.
|
||||
*/
|
||||
static int iFileGetc (void)
|
||||
{
|
||||
int c;
|
||||
readnext:
|
||||
c = getc (File.fp);
|
||||
|
||||
/* If previous character was a newline, then we're starting a line.
|
||||
*/
|
||||
if (File.newLine && c != EOF)
|
||||
{
|
||||
fileNewline ();
|
||||
if (c == '#' && Option.lineDirectives)
|
||||
{
|
||||
if (parseLineDirective ())
|
||||
goto readnext;
|
||||
else
|
||||
{
|
||||
fsetpos (File.fp, &StartOfLine);
|
||||
c = getc (File.fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == EOF)
|
||||
File.eof = TRUE;
|
||||
else if (c == NEWLINE)
|
||||
{
|
||||
File.newLine = TRUE;
|
||||
fgetpos (File.fp, &StartOfLine);
|
||||
}
|
||||
else if (c == CRETURN)
|
||||
{
|
||||
/* Turn line breaks into a canonical form. The three commonly
|
||||
* used forms if line breaks: LF (UNIX), CR (MacIntosh), and
|
||||
* CR-LF (MS-DOS) are converted into a generic newline.
|
||||
*/
|
||||
const int next = getc (File.fp); /* is CR followed by LF? */
|
||||
|
||||
if (next != NEWLINE)
|
||||
ungetc (next, File.fp);
|
||||
|
||||
c = NEWLINE; /* convert CR into newline */
|
||||
File.newLine = TRUE;
|
||||
fgetpos (File.fp, &StartOfLine);
|
||||
}
|
||||
DebugStatement ( debugPutc (DEBUG_RAW, c); )
|
||||
return c;
|
||||
}
|
||||
|
||||
extern void fileUngetc (int c)
|
||||
{
|
||||
File.ungetch = c;
|
||||
}
|
||||
|
||||
static vString *iFileGetLine (void)
|
||||
{
|
||||
vString *result = NULL;
|
||||
int c;
|
||||
if (File.line == NULL)
|
||||
File.line = vStringNew ();
|
||||
vStringClear (File.line);
|
||||
do
|
||||
{
|
||||
c = iFileGetc ();
|
||||
if (c != EOF)
|
||||
vStringPut (File.line, c);
|
||||
if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
|
||||
{
|
||||
vStringTerminate (File.line);
|
||||
#ifdef HAVE_REGEX
|
||||
if (vStringLength (File.line) > 0)
|
||||
matchRegex (File.line, File.source.language);
|
||||
#endif
|
||||
result = File.line;
|
||||
break;
|
||||
}
|
||||
} while (c != EOF);
|
||||
Assert (result != NULL || File.eof);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Do not mix use of fileReadLine () and fileGetc () for the same file.
|
||||
*/
|
||||
extern int fileGetc (void)
|
||||
{
|
||||
int c;
|
||||
|
||||
/* If there is an ungotten character, then return it. Don't do any
|
||||
* other processing on it, though, because we already did that the
|
||||
* first time it was read through fileGetc ().
|
||||
*/
|
||||
if (File.ungetch != '\0')
|
||||
{
|
||||
c = File.ungetch;
|
||||
File.ungetch = '\0';
|
||||
return c; /* return here to avoid re-calling debugPutc () */
|
||||
}
|
||||
do
|
||||
{
|
||||
if (File.currentLine != NULL)
|
||||
{
|
||||
c = *File.currentLine++;
|
||||
if (c == '\0')
|
||||
File.currentLine = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
vString* const line = iFileGetLine ();
|
||||
if (line != NULL)
|
||||
File.currentLine = (unsigned char*) vStringValue (line);
|
||||
if (File.currentLine == NULL)
|
||||
c = EOF;
|
||||
else
|
||||
c = '\0';
|
||||
}
|
||||
} while (c == '\0');
|
||||
DebugStatement ( debugPutc (DEBUG_READ, c); )
|
||||
return c;
|
||||
}
|
||||
|
||||
/* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
|
||||
* and fileGetc() for the same file. The returned string does not contain
|
||||
* the terminating newline. A NULL return value means that all lines in the
|
||||
* file have been read and we are at the end of file.
|
||||
*/
|
||||
extern const unsigned char *fileReadLine (void)
|
||||
{
|
||||
vString* const line = iFileGetLine ();
|
||||
const unsigned char* result = NULL;
|
||||
if (line != NULL)
|
||||
{
|
||||
result = (const unsigned char*) vStringValue (line);
|
||||
vStringStripNewline (line);
|
||||
DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Source file line reading with automatic buffer sizing
|
||||
*/
|
||||
extern char *readLine (vString *const vLine, FILE *const fp)
|
||||
{
|
||||
char *result = NULL;
|
||||
|
||||
vStringClear (vLine);
|
||||
if (fp == NULL) /* to free memory allocated to buffer */
|
||||
error (FATAL, "NULL file pointer");
|
||||
else
|
||||
{
|
||||
boolean reReadLine;
|
||||
|
||||
/* If reading the line places any character other than a null or a
|
||||
* newline at the last character position in the buffer (one less
|
||||
* than the buffer size), then we must resize the buffer and
|
||||
* reattempt to read the line.
|
||||
*/
|
||||
do
|
||||
{
|
||||
char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2;
|
||||
fpos_t startOfLine;
|
||||
|
||||
fgetpos (fp, &startOfLine);
|
||||
reReadLine = FALSE;
|
||||
*pLastChar = '\0';
|
||||
result = fgets (vStringValue (vLine), (int) vStringSize (vLine), fp);
|
||||
if (result == NULL)
|
||||
{
|
||||
if (! feof (fp))
|
||||
error (FATAL | PERROR, "Failure on attempt to read file");
|
||||
}
|
||||
else if (*pLastChar != '\0' &&
|
||||
*pLastChar != '\n' && *pLastChar != '\r')
|
||||
{
|
||||
/* buffer overflow */
|
||||
reReadLine = vStringAutoResize (vLine);
|
||||
if (reReadLine)
|
||||
fsetpos (fp, &startOfLine);
|
||||
else
|
||||
error (FATAL | PERROR, "input line too big; out of memory");
|
||||
}
|
||||
else
|
||||
{
|
||||
char* eol;
|
||||
vStringSetLength (vLine);
|
||||
/* canonicalize new line */
|
||||
eol = vStringValue (vLine) + vStringLength (vLine) - 1;
|
||||
if (*eol == '\r')
|
||||
*eol = '\n';
|
||||
else if (*(eol - 1) == '\r' && *eol == '\n')
|
||||
{
|
||||
*(eol - 1) = '\n';
|
||||
*eol = '\0';
|
||||
--vLine->length;
|
||||
}
|
||||
}
|
||||
} while (reReadLine);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Places into the line buffer the contents of the line referenced by
|
||||
* "location".
|
||||
*/
|
||||
extern char *readSourceLine (
|
||||
vString *const vLine, fpos_t location, long *const pSeekValue)
|
||||
{
|
||||
fpos_t orignalPosition;
|
||||
char *result;
|
||||
|
||||
fgetpos (File.fp, &orignalPosition);
|
||||
fsetpos (File.fp, &location);
|
||||
if (pSeekValue != NULL)
|
||||
*pSeekValue = ftell (File.fp);
|
||||
result = readLine (vLine, File.fp);
|
||||
if (result == NULL)
|
||||
error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
|
||||
fsetpos (File.fp, &orignalPosition);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* $Id: read.h,v 1.5 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to read.c
|
||||
*/
|
||||
#ifndef _READ_H
|
||||
#define _READ_H
|
||||
|
||||
#if defined(FILE_WRITE) || defined(VAXC)
|
||||
# define CONST_FILE
|
||||
#else
|
||||
# define CONST_FILE const
|
||||
#endif
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define getInputLineNumber() File.lineNumber
|
||||
#define getInputFileName() vStringValue (File.source.name)
|
||||
#define getInputFilePosition() File.filePosition
|
||||
#define getSourceFileName() vStringValue (File.source.name)
|
||||
#define getSourceFileTagPath() File.source.tagPath
|
||||
#define getSourceLanguage() File.source.language
|
||||
#define getSourceLanguageName() getLanguageName (File.source.language)
|
||||
#define getSourceLineNumber() File.source.lineNumber
|
||||
#define isLanguage(lang) (boolean)((lang) == File.source.language)
|
||||
#define isHeaderFile() File.source.isHeader
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
enum eCharacters {
|
||||
/* white space characters */
|
||||
SPACE = ' ',
|
||||
NEWLINE = '\n',
|
||||
CRETURN = '\r',
|
||||
FORMFEED = '\f',
|
||||
TAB = '\t',
|
||||
VTAB = '\v',
|
||||
|
||||
/* some hard to read characters */
|
||||
DOUBLE_QUOTE = '"',
|
||||
SINGLE_QUOTE = '\'',
|
||||
BACKSLASH = '\\',
|
||||
|
||||
STRING_SYMBOL = ('S' + 0x80),
|
||||
CHAR_SYMBOL = ('C' + 0x80)
|
||||
};
|
||||
|
||||
/* Maintains the state of the current source file.
|
||||
*/
|
||||
typedef struct sInputFile {
|
||||
vString *name; /* name of input file */
|
||||
vString *path; /* path of input file (if any) */
|
||||
vString *line; /* last line read from file */
|
||||
const unsigned char* currentLine; /* current line being worked on */
|
||||
FILE *fp; /* stream used for reading the file */
|
||||
unsigned long lineNumber; /* line number in the input file */
|
||||
fpos_t filePosition; /* file position of current line */
|
||||
int ungetch; /* a single character that was ungotten */
|
||||
boolean eof; /* have we reached the end of file? */
|
||||
boolean newLine; /* will the next character begin a new line? */
|
||||
langType language; /* language of input file */
|
||||
|
||||
/* Contains data pertaining to the original source file in which the tag
|
||||
* was defined. This may be different from the input file when #line
|
||||
* directives are processed (i.e. the input file is preprocessor output).
|
||||
*/
|
||||
struct sSource {
|
||||
vString *name; /* name to report for source file */
|
||||
char *tagPath; /* path of source file relative to tag file */
|
||||
unsigned long lineNumber;/* line number in the source file */
|
||||
boolean isHeader; /* is source file a header file? */
|
||||
langType language; /* language of source file */
|
||||
} source;
|
||||
} inputFile;
|
||||
|
||||
/*
|
||||
* GLOBAL VARIABLES
|
||||
*/
|
||||
extern CONST_FILE inputFile File;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void freeSourceFileResources (void);
|
||||
extern boolean fileOpen (const char *const fileName, const langType language);
|
||||
extern boolean fileEOF (void);
|
||||
extern void fileClose (void);
|
||||
extern int fileGetc (void);
|
||||
extern void fileUngetc (int c);
|
||||
extern const unsigned char *fileReadLine (void);
|
||||
extern char *readLine (vString *const vLine, FILE *const fp);
|
||||
extern char *readSourceLine (vString *const vLine, fpos_t location, long *const pSeekValue);
|
||||
|
||||
#endif /* _READ_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,958 @@
|
||||
/*
|
||||
* $Id: readtags.c,v 1.25 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released into the public domain.
|
||||
*
|
||||
* This module contains functions for reading tag files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h> /* to declare off_t */
|
||||
|
||||
#include "readtags.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define TAB '\t'
|
||||
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef struct {
|
||||
size_t size;
|
||||
char *buffer;
|
||||
} vstring;
|
||||
|
||||
/* Information about current tag file */
|
||||
struct sTagFile {
|
||||
/* has the file been opened and this structure initialized? */
|
||||
short initialized;
|
||||
/* format of tag file */
|
||||
short format;
|
||||
/* how is the tag file sorted? */
|
||||
sortType sortMethod;
|
||||
/* pointer to file structure */
|
||||
FILE* fp;
|
||||
/* file position of first character of `line' */
|
||||
off_t pos;
|
||||
/* size of tag file in seekable positions */
|
||||
off_t size;
|
||||
/* last line read */
|
||||
vstring line;
|
||||
/* name of tag in last line read */
|
||||
vstring name;
|
||||
/* defines tag search state */
|
||||
struct {
|
||||
/* file position of last match for tag */
|
||||
off_t pos;
|
||||
/* name of tag last searched for */
|
||||
const char *name;
|
||||
/* length of name for partial matches */
|
||||
size_t nameLength;
|
||||
/* peforming partial match */
|
||||
short partial;
|
||||
/* ignoring case */
|
||||
short ignorecase;
|
||||
} search;
|
||||
/* miscellaneous extension fields */
|
||||
struct {
|
||||
/* number of entries in `list' */
|
||||
unsigned short max;
|
||||
/* list of key value pairs */
|
||||
tagExtensionField *list;
|
||||
} fields;
|
||||
/* buffers to be freed at close */
|
||||
struct {
|
||||
/* name of program author */
|
||||
char *author;
|
||||
/* name of program */
|
||||
char *name;
|
||||
/* URL of distribution */
|
||||
char *url;
|
||||
/* program version */
|
||||
char *version;
|
||||
} program;
|
||||
};
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
const char *const EmptyString = "";
|
||||
const char *const PseudoTagPrefix = "!_";
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compare two strings, ignoring case.
|
||||
* Return 0 for match, < 0 for smaller, > 0 for bigger
|
||||
* Make sure case is folded to uppercase in comparison (like for 'sort -f')
|
||||
* This makes a difference when one of the chars lies between upper and lower
|
||||
* ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
|
||||
*/
|
||||
static int struppercmp (const char *s1, const char *s2)
|
||||
{
|
||||
int result;
|
||||
do
|
||||
{
|
||||
result = toupper ((int) *s1) - toupper ((int) *s2);
|
||||
} while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
static int strnuppercmp (const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
int result;
|
||||
do
|
||||
{
|
||||
result = toupper ((int) *s1) - toupper ((int) *s2);
|
||||
} while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
static int growString (vstring *s)
|
||||
{
|
||||
int result = 0;
|
||||
size_t newLength;
|
||||
char *newLine;
|
||||
if (s->size == 0)
|
||||
{
|
||||
newLength = 128;
|
||||
newLine = (char*) malloc (newLength);
|
||||
*newLine = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
newLength = 2 * s->size;
|
||||
newLine = (char*) realloc (s->buffer, newLength);
|
||||
}
|
||||
if (newLine == NULL)
|
||||
perror ("string too large");
|
||||
else
|
||||
{
|
||||
s->buffer = newLine;
|
||||
s->size = newLength;
|
||||
result = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Copy name of tag out of tag line */
|
||||
static void copyName (tagFile *const file)
|
||||
{
|
||||
size_t length;
|
||||
const char *end = strchr (file->line.buffer, '\t');
|
||||
if (end == NULL)
|
||||
{
|
||||
end = strchr (file->line.buffer, '\n');
|
||||
if (end == NULL)
|
||||
end = strchr (file->line.buffer, '\r');
|
||||
}
|
||||
if (end != NULL)
|
||||
length = end - file->line.buffer;
|
||||
else
|
||||
length = strlen (file->line.buffer);
|
||||
while (length >= file->name.size)
|
||||
growString (&file->name);
|
||||
strncpy (file->name.buffer, file->line.buffer, length);
|
||||
file->name.buffer [length] = '\0';
|
||||
}
|
||||
|
||||
static int readTagLineRaw (tagFile *const file)
|
||||
{
|
||||
int result = 1;
|
||||
int reReadLine;
|
||||
|
||||
/* If reading the line places any character other than a null or a
|
||||
* newline at the last character position in the buffer (one less than
|
||||
* the buffer size), then we must resize the buffer and reattempt to read
|
||||
* the line.
|
||||
*/
|
||||
do
|
||||
{
|
||||
char *const pLastChar = file->line.buffer + file->line.size - 2;
|
||||
char *line;
|
||||
|
||||
file->pos = ftell (file->fp);
|
||||
reReadLine = 0;
|
||||
*pLastChar = '\0';
|
||||
line = fgets (file->line.buffer, (int) file->line.size, file->fp);
|
||||
if (line == NULL)
|
||||
{
|
||||
/* read error */
|
||||
if (! feof (file->fp))
|
||||
perror ("readTagLine");
|
||||
result = 0;
|
||||
}
|
||||
else if (*pLastChar != '\0' &&
|
||||
*pLastChar != '\n' && *pLastChar != '\r')
|
||||
{
|
||||
/* buffer overflow */
|
||||
growString (&file->line);
|
||||
fseek (file->fp, file->pos, SEEK_SET);
|
||||
reReadLine = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t i = strlen (file->line.buffer);
|
||||
while (i > 0 &&
|
||||
(file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r'))
|
||||
{
|
||||
file->line.buffer [i - 1] = '\0';
|
||||
--i;
|
||||
}
|
||||
}
|
||||
} while (reReadLine && result);
|
||||
if (result)
|
||||
copyName (file);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int readTagLine (tagFile *const file)
|
||||
{
|
||||
int result;
|
||||
do
|
||||
{
|
||||
result = readTagLineRaw (file);
|
||||
} while (result && *file->name.buffer == '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
static tagResult growFields (tagFile *const file)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
unsigned short newCount = 2 * file->fields.max;
|
||||
tagExtensionField *newFields = (tagExtensionField*)
|
||||
realloc (file->fields.list, newCount * sizeof (tagExtensionField));
|
||||
if (newFields == NULL)
|
||||
perror ("too many extension fields");
|
||||
else
|
||||
{
|
||||
file->fields.list = newFields;
|
||||
file->fields.max = newCount;
|
||||
result = TagSuccess;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void parseExtensionFields (tagFile *const file, tagEntry *const entry,
|
||||
char *const string)
|
||||
{
|
||||
char *p = string;
|
||||
while (p != NULL && *p != '\0')
|
||||
{
|
||||
while (*p == TAB)
|
||||
*p++ = '\0';
|
||||
if (*p != '\0')
|
||||
{
|
||||
char *colon;
|
||||
char *field = p;
|
||||
p = strchr (p, TAB);
|
||||
if (p != NULL)
|
||||
*p++ = '\0';
|
||||
colon = strchr (field, ':');
|
||||
if (colon == NULL)
|
||||
entry->kind = field;
|
||||
else
|
||||
{
|
||||
const char *key = field;
|
||||
const char *value = colon + 1;
|
||||
*colon = '\0';
|
||||
if (strcmp (key, "kind") == 0)
|
||||
entry->kind = value;
|
||||
else if (strcmp (key, "file") == 0)
|
||||
entry->fileScope = 1;
|
||||
else if (strcmp (key, "line") == 0)
|
||||
entry->address.lineNumber = atol (value);
|
||||
else
|
||||
{
|
||||
if (entry->fields.count == file->fields.max)
|
||||
growFields (file);
|
||||
file->fields.list [entry->fields.count].key = key;
|
||||
file->fields.list [entry->fields.count].value = value;
|
||||
++entry->fields.count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parseTagLine (tagFile *file, tagEntry *const entry)
|
||||
{
|
||||
int i;
|
||||
char *p = file->line.buffer;
|
||||
char *tab = strchr (p, TAB);
|
||||
int fieldsPresent = 0;
|
||||
|
||||
entry->fields.list = NULL;
|
||||
entry->fields.count = 0;
|
||||
entry->kind = NULL;
|
||||
entry->fileScope = 0;
|
||||
|
||||
entry->name = p;
|
||||
if (tab != NULL)
|
||||
{
|
||||
*tab = '\0';
|
||||
p = tab + 1;
|
||||
entry->file = p;
|
||||
tab = strchr (p, TAB);
|
||||
if (tab != NULL)
|
||||
{
|
||||
*tab = '\0';
|
||||
p = tab + 1;
|
||||
if (*p == '/' || *p == '?')
|
||||
{
|
||||
/* parse pattern */
|
||||
int delimiter = *(unsigned char*) p;
|
||||
entry->address.lineNumber = 0;
|
||||
entry->address.pattern = p;
|
||||
do
|
||||
{
|
||||
p = strchr (p + 1, delimiter);
|
||||
} while (p != NULL && *(p - 1) == '\\');
|
||||
if (p == NULL)
|
||||
{
|
||||
/* invalid pattern */
|
||||
}
|
||||
else
|
||||
++p;
|
||||
}
|
||||
else if (isdigit ((int) *(unsigned char*) p))
|
||||
{
|
||||
/* parse line number */
|
||||
entry->address.pattern = p;
|
||||
entry->address.lineNumber = atol (p);
|
||||
while (isdigit ((int) *(unsigned char*) p))
|
||||
++p;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid pattern */
|
||||
}
|
||||
fieldsPresent = (strncmp (p, ";\"", 2) == 0);
|
||||
*p = '\0';
|
||||
if (fieldsPresent)
|
||||
parseExtensionFields (file, entry, p + 2);
|
||||
}
|
||||
}
|
||||
if (entry->fields.count > 0)
|
||||
entry->fields.list = file->fields.list;
|
||||
for (i = entry->fields.count ; i < file->fields.max ; ++i)
|
||||
{
|
||||
file->fields.list [i].key = NULL;
|
||||
file->fields.list [i].value = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char *duplicate (const char *str)
|
||||
{
|
||||
char *result = NULL;
|
||||
if (str != NULL)
|
||||
{
|
||||
result = (char*) malloc (strlen (str) + 1);
|
||||
if (result == NULL)
|
||||
perror (NULL);
|
||||
else
|
||||
strcpy (result, str);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void readPseudoTags (tagFile *const file, tagFileInfo *const info)
|
||||
{
|
||||
fpos_t startOfLine;
|
||||
const size_t prefixLength = strlen (PseudoTagPrefix);
|
||||
if (info != NULL)
|
||||
{
|
||||
info->file.format = 1;
|
||||
info->file.sort = TAG_UNSORTED;
|
||||
info->program.author = NULL;
|
||||
info->program.name = NULL;
|
||||
info->program.url = NULL;
|
||||
info->program.version = NULL;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
fgetpos (file->fp, &startOfLine);
|
||||
if (! readTagLine (file))
|
||||
break;
|
||||
if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
|
||||
break;
|
||||
else
|
||||
{
|
||||
tagEntry entry;
|
||||
const char *key, *value;
|
||||
parseTagLine (file, &entry);
|
||||
key = entry.name + prefixLength;
|
||||
value = entry.file;
|
||||
if (strcmp (key, "TAG_FILE_SORTED") == 0)
|
||||
file->sortMethod = (sortType) atoi (value);
|
||||
else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
|
||||
file->format = atoi (value);
|
||||
else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
|
||||
file->program.author = duplicate (value);
|
||||
else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
|
||||
file->program.name = duplicate (value);
|
||||
else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
|
||||
file->program.url = duplicate (value);
|
||||
else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
|
||||
file->program.version = duplicate (value);
|
||||
if (info != NULL)
|
||||
{
|
||||
info->file.format = file->format;
|
||||
info->file.sort = file->sortMethod;
|
||||
info->program.author = file->program.author;
|
||||
info->program.name = file->program.name;
|
||||
info->program.url = file->program.url;
|
||||
info->program.version = file->program.version;
|
||||
}
|
||||
}
|
||||
}
|
||||
fsetpos (file->fp, &startOfLine);
|
||||
}
|
||||
|
||||
static void gotoFirstLogicalTag (tagFile *const file)
|
||||
{
|
||||
fpos_t startOfLine;
|
||||
const size_t prefixLength = strlen (PseudoTagPrefix);
|
||||
rewind (file->fp);
|
||||
while (1)
|
||||
{
|
||||
fgetpos (file->fp, &startOfLine);
|
||||
if (! readTagLine (file))
|
||||
break;
|
||||
if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
|
||||
break;
|
||||
}
|
||||
fsetpos (file->fp, &startOfLine);
|
||||
}
|
||||
|
||||
static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
|
||||
{
|
||||
tagFile *result = (tagFile*) malloc (sizeof (tagFile));
|
||||
if (result != NULL)
|
||||
{
|
||||
memset (result, 0, sizeof (tagFile));
|
||||
growString (&result->line);
|
||||
growString (&result->name);
|
||||
result->fields.max = 20;
|
||||
result->fields.list = (tagExtensionField*) malloc (
|
||||
result->fields.max * sizeof (tagExtensionField));
|
||||
result->fp = fopen (filePath, "r");
|
||||
if (result->fp == NULL)
|
||||
{
|
||||
free (result);
|
||||
result = NULL;
|
||||
info->status.error_number = errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek (result->fp, 0, SEEK_END);
|
||||
result->size = ftell (result->fp);
|
||||
rewind (result->fp);
|
||||
readPseudoTags (result, info);
|
||||
info->status.opened = 1;
|
||||
result->initialized = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void terminate (tagFile *const file)
|
||||
{
|
||||
fclose (file->fp);
|
||||
|
||||
free (file->line.buffer);
|
||||
free (file->name.buffer);
|
||||
free (file->fields.list);
|
||||
|
||||
if (file->program.author != NULL)
|
||||
free (file->program.author);
|
||||
if (file->program.name != NULL)
|
||||
free (file->program.name);
|
||||
if (file->program.url != NULL)
|
||||
free (file->program.url);
|
||||
if (file->program.version != NULL)
|
||||
free (file->program.version);
|
||||
|
||||
memset (file, 0, sizeof (tagFile));
|
||||
|
||||
free (file);
|
||||
}
|
||||
|
||||
static tagResult readNext (tagFile *const file, tagEntry *const entry)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file == NULL || ! file->initialized)
|
||||
result = TagFailure;
|
||||
else if (! readTagLine (file))
|
||||
result = TagFailure;
|
||||
else
|
||||
{
|
||||
if (entry != NULL)
|
||||
parseTagLine (file, entry);
|
||||
result = TagSuccess;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char *readFieldValue (
|
||||
const tagEntry *const entry, const char *const key)
|
||||
{
|
||||
const char *result = NULL;
|
||||
int i;
|
||||
if (strcmp (key, "kind") == 0)
|
||||
result = entry->kind;
|
||||
else if (strcmp (key, "file") == 0)
|
||||
result = EmptyString;
|
||||
else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i)
|
||||
if (strcmp (entry->fields.list [i].key, key) == 0)
|
||||
result = entry->fields.list [i].value;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int readTagLineSeek (tagFile *const file, const off_t pos)
|
||||
{
|
||||
int result = 0;
|
||||
if (fseek (file->fp, pos, SEEK_SET) == 0)
|
||||
{
|
||||
result = readTagLine (file); /* read probable partial line */
|
||||
if (pos > 0 && result)
|
||||
result = readTagLine (file); /* read complete line */
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int nameComparison (tagFile *const file)
|
||||
{
|
||||
int result;
|
||||
if (file->search.ignorecase)
|
||||
{
|
||||
if (file->search.partial)
|
||||
result = strnuppercmp (file->search.name, file->name.buffer,
|
||||
file->search.nameLength);
|
||||
else
|
||||
result = struppercmp (file->search.name, file->name.buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file->search.partial)
|
||||
result = strncmp (file->search.name, file->name.buffer,
|
||||
file->search.nameLength);
|
||||
else
|
||||
result = strcmp (file->search.name, file->name.buffer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void findFirstNonMatchBefore (tagFile *const file)
|
||||
{
|
||||
#define JUMP_BACK 512
|
||||
int more_lines;
|
||||
int comp;
|
||||
off_t start = file->pos;
|
||||
off_t pos = start;
|
||||
do
|
||||
{
|
||||
if (pos < (off_t) JUMP_BACK)
|
||||
pos = 0;
|
||||
else
|
||||
pos = pos - JUMP_BACK;
|
||||
more_lines = readTagLineSeek (file, pos);
|
||||
comp = nameComparison (file);
|
||||
} while (more_lines && comp == 0 && pos > 0 && pos < start);
|
||||
}
|
||||
|
||||
static tagResult findFirstMatchBefore (tagFile *const file)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
int more_lines;
|
||||
off_t start = file->pos;
|
||||
findFirstNonMatchBefore (file);
|
||||
do
|
||||
{
|
||||
more_lines = readTagLine (file);
|
||||
if (nameComparison (file) == 0)
|
||||
result = TagSuccess;
|
||||
} while (more_lines && result != TagSuccess && file->pos < start);
|
||||
return result;
|
||||
}
|
||||
|
||||
static tagResult findBinary (tagFile *const file)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
off_t lower_limit = 0;
|
||||
off_t upper_limit = file->size;
|
||||
off_t last_pos = 0;
|
||||
off_t pos = upper_limit / 2;
|
||||
while (result != TagSuccess)
|
||||
{
|
||||
if (! readTagLineSeek (file, pos))
|
||||
{
|
||||
/* in case we fell off end of file */
|
||||
result = findFirstMatchBefore (file);
|
||||
break;
|
||||
}
|
||||
else if (pos == last_pos)
|
||||
{
|
||||
/* prevent infinite loop if we backed up to beginning of file */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int comp = nameComparison (file);
|
||||
last_pos = pos;
|
||||
if (comp < 0)
|
||||
{
|
||||
upper_limit = pos;
|
||||
pos = lower_limit + ((upper_limit - lower_limit) / 2);
|
||||
}
|
||||
else if (comp > 0)
|
||||
{
|
||||
lower_limit = pos;
|
||||
pos = lower_limit + ((upper_limit - lower_limit) / 2);
|
||||
}
|
||||
else if (pos == 0)
|
||||
result = TagSuccess;
|
||||
else
|
||||
result = findFirstMatchBefore (file);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static tagResult findSequential (tagFile *const file)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file->initialized)
|
||||
{
|
||||
while (result == TagFailure && readTagLine (file))
|
||||
{
|
||||
if (nameComparison (file) == 0)
|
||||
result = TagSuccess;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static tagResult find (tagFile *const file, tagEntry *const entry,
|
||||
const char *const name, const int options)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
file->search.name = name;
|
||||
file->search.nameLength = strlen (name);
|
||||
file->search.partial = (options & TAG_PARTIALMATCH) != 0;
|
||||
file->search.ignorecase = (options & TAG_IGNORECASE) != 0;
|
||||
fseek (file->fp, 0, SEEK_END);
|
||||
file->size = ftell (file->fp);
|
||||
rewind (file->fp);
|
||||
if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
|
||||
(file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf ("<performing binary search>\n");
|
||||
#endif
|
||||
result = findBinary (file);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf ("<performing sequential search>\n");
|
||||
#endif
|
||||
result = findSequential (file);
|
||||
}
|
||||
|
||||
if (result != TagSuccess)
|
||||
file->search.pos = file->size;
|
||||
else
|
||||
{
|
||||
file->search.pos = file->pos;
|
||||
if (entry != NULL)
|
||||
parseTagLine (file, entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static tagResult findNext (tagFile *const file, tagEntry *const entry)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
|
||||
(file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
|
||||
{
|
||||
result = tagsNext (file, entry);
|
||||
if (result == TagSuccess && nameComparison (file) != 0)
|
||||
result = TagFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = findSequential (file);
|
||||
if (result == TagSuccess && entry != NULL)
|
||||
parseTagLine (file, entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* EXTERNAL INTERFACE
|
||||
*/
|
||||
|
||||
extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info)
|
||||
{
|
||||
return initialize (filePath, info);
|
||||
}
|
||||
|
||||
extern tagResult tagsSetSortType (tagFile *const file, const sortType type)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file != NULL && file->initialized)
|
||||
{
|
||||
file->sortMethod = type;
|
||||
result = TagSuccess;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file != NULL && file->initialized)
|
||||
{
|
||||
gotoFirstLogicalTag (file);
|
||||
result = readNext (file, entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern tagResult tagsNext (tagFile *const file, tagEntry *const entry)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file != NULL && file->initialized)
|
||||
result = readNext (file, entry);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern const char *tagsField (const tagEntry *const entry, const char *const key)
|
||||
{
|
||||
const char *result = NULL;
|
||||
if (entry != NULL)
|
||||
result = readFieldValue (entry, key);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern tagResult tagsFind (tagFile *const file, tagEntry *const entry,
|
||||
const char *const name, const int options)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file != NULL && file->initialized)
|
||||
result = find (file, entry, name, options);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file != NULL && file->initialized)
|
||||
result = findNext (file, entry);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern tagResult tagsClose (tagFile *const file)
|
||||
{
|
||||
tagResult result = TagFailure;
|
||||
if (file != NULL && file->initialized)
|
||||
{
|
||||
terminate (file);
|
||||
result = TagSuccess;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST FRAMEWORK
|
||||
*/
|
||||
|
||||
#ifdef READTAGS_MAIN
|
||||
|
||||
static const char *TagFileName = "tags";
|
||||
static const char *ProgramName;
|
||||
static int extensionFields;
|
||||
static int SortOverride;
|
||||
static sortType SortMethod;
|
||||
|
||||
static void printTag (const tagEntry *entry)
|
||||
{
|
||||
int i;
|
||||
int first = 1;
|
||||
const char* separator = ";\"";
|
||||
const char* const empty = "";
|
||||
/* "sep" returns a value only the first time it is evaluated */
|
||||
#define sep (first ? (first = 0, separator) : empty)
|
||||
printf ("%s\t%s\t%s",
|
||||
entry->name, entry->file, entry->address.pattern);
|
||||
if (extensionFields)
|
||||
{
|
||||
if (entry->kind != NULL && entry->kind [0] != '\0')
|
||||
printf ("%s\tkind:%s", sep, entry->kind);
|
||||
if (entry->fileScope)
|
||||
printf ("%s\tfile:", sep);
|
||||
#if 0
|
||||
if (entry->address.lineNumber > 0)
|
||||
printf ("%s\tline:%lu", sep, entry->address.lineNumber);
|
||||
#endif
|
||||
for (i = 0 ; i < entry->fields.count ; ++i)
|
||||
printf ("%s\t%s:%s", sep, entry->fields.list [i].key,
|
||||
entry->fields.list [i].value);
|
||||
}
|
||||
putchar ('\n');
|
||||
#undef sep
|
||||
}
|
||||
|
||||
static void findTag (const char *const name, const int options)
|
||||
{
|
||||
tagFileInfo info;
|
||||
tagEntry entry;
|
||||
tagFile *const file = tagsOpen (TagFileName, &info);
|
||||
if (file == NULL)
|
||||
{
|
||||
fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
|
||||
ProgramName, strerror (info.status.error_number), name);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SortOverride)
|
||||
tagsSetSortType (file, SortMethod);
|
||||
if (tagsFind (file, &entry, name, options) == TagSuccess)
|
||||
{
|
||||
do
|
||||
{
|
||||
printTag (&entry);
|
||||
} while (tagsFindNext (file, &entry) == TagSuccess);
|
||||
}
|
||||
tagsClose (file);
|
||||
}
|
||||
}
|
||||
|
||||
static void listTags (void)
|
||||
{
|
||||
tagFileInfo info;
|
||||
tagEntry entry;
|
||||
tagFile *const file = tagsOpen (TagFileName, &info);
|
||||
if (file == NULL)
|
||||
{
|
||||
fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
|
||||
ProgramName, strerror (info.status.error_number), TagFileName);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (tagsNext (file, &entry) == TagSuccess)
|
||||
printTag (&entry);
|
||||
tagsClose (file);
|
||||
}
|
||||
}
|
||||
|
||||
const char *const Usage =
|
||||
"Find tag file entries matching specified names.\n\n"
|
||||
"Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n"
|
||||
"Options:\n"
|
||||
" -e Include extension fields in output.\n"
|
||||
" -i Perform case-insensitive matching.\n"
|
||||
" -l List all tags.\n"
|
||||
" -p Perform partial matching.\n"
|
||||
" -s[0|1|2] Override sort detection of tag file.\n"
|
||||
" -t file Use specified tag file (default: \"tags\").\n"
|
||||
"Note that options are acted upon as encountered, so order is significant.\n";
|
||||
|
||||
extern int main (int argc, char **argv)
|
||||
{
|
||||
int options = 0;
|
||||
int actionSupplied = 0;
|
||||
int i;
|
||||
ProgramName = argv [0];
|
||||
if (argc == 1)
|
||||
{
|
||||
fprintf (stderr, Usage, ProgramName);
|
||||
exit (1);
|
||||
}
|
||||
for (i = 1 ; i < argc ; ++i)
|
||||
{
|
||||
const char *const arg = argv [i];
|
||||
if (arg [0] != '-')
|
||||
{
|
||||
findTag (arg, options);
|
||||
actionSupplied = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t j;
|
||||
for (j = 1 ; arg [j] != '\0' ; ++j)
|
||||
{
|
||||
switch (arg [j])
|
||||
{
|
||||
case 'e': extensionFields = 1; break;
|
||||
case 'i': options |= TAG_IGNORECASE; break;
|
||||
case 'p': options |= TAG_PARTIALMATCH; break;
|
||||
case 'l': listTags (); actionSupplied = 1; break;
|
||||
|
||||
case 't':
|
||||
if (arg [j+1] != '\0')
|
||||
{
|
||||
TagFileName = arg + j + 1;
|
||||
j += strlen (TagFileName);
|
||||
}
|
||||
else if (i + 1 < argc)
|
||||
TagFileName = argv [++i];
|
||||
else
|
||||
{
|
||||
fprintf (stderr, Usage, ProgramName);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
SortOverride = 1;
|
||||
++j;
|
||||
if (arg [j] == '\0')
|
||||
SortMethod = TAG_SORTED;
|
||||
else if (strchr ("012", arg[j]) != NULL)
|
||||
SortMethod = (sortType) (arg[j] - '0');
|
||||
else
|
||||
{
|
||||
fprintf (stderr, Usage, ProgramName);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr, "%s: unknown option: %c\n",
|
||||
ProgramName, arg[j]);
|
||||
exit (1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! actionSupplied)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"%s: no action specified: specify tag name(s) or -l option\n",
|
||||
ProgramName);
|
||||
exit (1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* $Id: readtags.h,v 1.13 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for the public domain.
|
||||
*
|
||||
* This file defines the public interface for looking up tag entries in tag
|
||||
* files.
|
||||
*
|
||||
* The functions defined in this interface are intended to provide tag file
|
||||
* support to a software tool. The tag lookups provided are sufficiently fast
|
||||
* enough to permit opening a sorted tag file, searching for a matching tag,
|
||||
* then closing the tag file each time a tag is looked up (search times are
|
||||
* on the order of hundreths of a second, even for huge tag files). This is
|
||||
* the recommended use of this library for most tool applications. Adhering
|
||||
* to this approach permits a user to regenerate a tag file at will without
|
||||
* the tool needing to detect and resynchronize with changes to the tag file.
|
||||
* Even for an unsorted 24MB tag file, tag searches take about one second.
|
||||
*/
|
||||
#ifndef READTAGS_H
|
||||
#define READTAGS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
|
||||
/* Options for tagsSetSortType() */
|
||||
typedef enum {
|
||||
TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED
|
||||
} sortType ;
|
||||
|
||||
/* Options for tagsFind() */
|
||||
#define TAG_FULLMATCH 0x0
|
||||
#define TAG_PARTIALMATCH 0x1
|
||||
|
||||
#define TAG_OBSERVECASE 0x0
|
||||
#define TAG_IGNORECASE 0x2
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
typedef enum { TagFailure = 0, TagSuccess = 1 } tagResult;
|
||||
|
||||
struct sTagFile;
|
||||
|
||||
typedef struct sTagFile tagFile;
|
||||
|
||||
/* This structure contains information about the tag file. */
|
||||
typedef struct {
|
||||
|
||||
struct {
|
||||
/* was the tag file successfully opened? */
|
||||
int opened;
|
||||
|
||||
/* errno value when 'opened' is false */
|
||||
int error_number;
|
||||
} status;
|
||||
|
||||
/* information about the structure of the tag file */
|
||||
struct {
|
||||
/* format of tag file (1 = original, 2 = extended) */
|
||||
short format;
|
||||
|
||||
/* how is the tag file sorted? */
|
||||
sortType sort;
|
||||
} file;
|
||||
|
||||
|
||||
/* information about the program which created this tag file */
|
||||
struct {
|
||||
/* name of author of generating program (may be null) */
|
||||
const char *author;
|
||||
|
||||
/* name of program (may be null) */
|
||||
const char *name;
|
||||
|
||||
/* URL of distribution (may be null) */
|
||||
const char *url;
|
||||
|
||||
/* program version (may be null) */
|
||||
const char *version;
|
||||
} program;
|
||||
|
||||
} tagFileInfo;
|
||||
|
||||
/* This structure contains information about an extension field for a tag.
|
||||
* These exist at the end of the tag in the form "key:value").
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
/* the key of the extension field */
|
||||
const char *key;
|
||||
|
||||
/* the value of the extension field (may be an empty string) */
|
||||
const char *value;
|
||||
|
||||
} tagExtensionField;
|
||||
|
||||
/* This structure contains information about a specific tag. */
|
||||
typedef struct {
|
||||
|
||||
/* name of tag */
|
||||
const char *name;
|
||||
|
||||
/* path of source file containing definition of tag */
|
||||
const char *file;
|
||||
|
||||
/* address for locating tag in source file */
|
||||
struct {
|
||||
/* pattern for locating source line
|
||||
* (may be NULL if not present) */
|
||||
const char *pattern;
|
||||
|
||||
/* line number in source file of tag definition
|
||||
* (may be zero if not known) */
|
||||
unsigned long lineNumber;
|
||||
} address;
|
||||
|
||||
/* kind of tag (may by name, character, or NULL if not known) */
|
||||
const char *kind;
|
||||
|
||||
/* is tag of file-limited scope? */
|
||||
short fileScope;
|
||||
|
||||
/* miscellaneous extension fields */
|
||||
struct {
|
||||
/* number of entries in `list' */
|
||||
unsigned short count;
|
||||
|
||||
/* list of key value pairs */
|
||||
tagExtensionField *list;
|
||||
} fields;
|
||||
|
||||
} tagEntry;
|
||||
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
|
||||
/*
|
||||
* This function must be called before calling other functions in this
|
||||
* library. It is passed the path to the tag file to read and a (possibly
|
||||
* null) pointer to a structure which, if not null, will be populated with
|
||||
* information about the tag file. If successful, the function will return a
|
||||
* handle which must be supplied to other calls to read information from the
|
||||
* tag file, and info.status.opened will be set to true. If unsuccessful,
|
||||
* info.status.opened will be set to false and info.status.error_number will
|
||||
* be set to the errno value representing the system error preventing the tag
|
||||
* file from being successfully opened.
|
||||
*/
|
||||
extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info);
|
||||
|
||||
/*
|
||||
* This function allows the client to override the normal automatic detection
|
||||
* of how a tag file is sorted. Permissible values for `type' are
|
||||
* TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED. Tag files in the new extended
|
||||
* format contain a key indicating whether or not they are sorted. However,
|
||||
* tag files in the original format do not contain such a key even when
|
||||
* sorted, preventing this library from taking advantage of fast binary
|
||||
* lookups. If the client knows that such an unmarked tag file is indeed
|
||||
* sorted (or not), it can override the automatic detection. Note that
|
||||
* incorrect lookup results will result if a tag file is marked as sorted when
|
||||
* it actually is not. The function will return TagSuccess if called on an
|
||||
* open tag file or TagFailure if not.
|
||||
*/
|
||||
extern tagResult tagsSetSortType (tagFile *const file, const sortType type);
|
||||
|
||||
/*
|
||||
* Reads the first tag in the file, if any. It is passed the handle to an
|
||||
* opened tag file and a (possibly null) pointer to a structure which, if not
|
||||
* null, will be populated with information about the first tag file entry.
|
||||
* The function will return TagSuccess another tag entry is found, or
|
||||
* TagFailure if not (i.e. it reached end of file).
|
||||
*/
|
||||
extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry);
|
||||
|
||||
/*
|
||||
* Step to the next tag in the file, if any. It is passed the handle to an
|
||||
* opened tag file and a (possibly null) pointer to a structure which, if not
|
||||
* null, will be populated with information about the next tag file entry. The
|
||||
* function will return TagSuccess another tag entry is found, or TagFailure
|
||||
* if not (i.e. it reached end of file). It will always read the first tag in
|
||||
* the file immediately after calling tagsOpen().
|
||||
*/
|
||||
extern tagResult tagsNext (tagFile *const file, tagEntry *const entry);
|
||||
|
||||
/*
|
||||
* Retrieve the value associated with the extension field for a specified key.
|
||||
* It is passed a pointer to a structure already populated with values by a
|
||||
* previous call to tagsNext(), tagsFind(), or tagsFindNext(), and a string
|
||||
* containing the key of the desired extension field. If no such field of the
|
||||
* specified key exists, the function will return null.
|
||||
*/
|
||||
extern const char *tagsField (const tagEntry *const entry, const char *const key);
|
||||
|
||||
/*
|
||||
* Find the first tag matching `name'. The structure pointed to by `entry'
|
||||
* will be populated with information about the tag file entry. If a tag file
|
||||
* is sorted using the C locale, a binary search algorithm is used to search
|
||||
* the tag file, resulting in very fast tag lookups, even in huge tag files.
|
||||
* Various options controlling the matches can be combined by bit-wise or-ing
|
||||
* certain values together. The available values are:
|
||||
*
|
||||
* TAG_PARTIALMATCH
|
||||
* Tags whose leading characters match `name' will qualify.
|
||||
*
|
||||
* TAG_FULLMATCH
|
||||
* Only tags whose full lengths match `name' will qualify.
|
||||
*
|
||||
* TAG_IGNORECASE
|
||||
* Matching will be performed in a case-insenstive manner. Note that
|
||||
* this disables binary searches of the tag file.
|
||||
*
|
||||
* TAG_OBSERVECASE
|
||||
* Matching will be performed in a case-senstive manner. Note that
|
||||
* this enables binary searches of the tag file.
|
||||
*
|
||||
* The function will return TagSuccess if a tag matching the name is found, or
|
||||
* TagFailure if not.
|
||||
*/
|
||||
extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, const char *const name, const int options);
|
||||
|
||||
/*
|
||||
* Find the next tag matching the name and options supplied to the most recent
|
||||
* call to tagsFind() for the same tag file. The structure pointed to by
|
||||
* `entry' will be populated with information about the tag file entry. The
|
||||
* function will return TagSuccess if another tag matching the name is found,
|
||||
* or TagFailure if not.
|
||||
*/
|
||||
extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry);
|
||||
|
||||
/*
|
||||
* Call tagsTerminate() at completion of reading the tag file, which will
|
||||
* close the file and free any internal memory allocated. The function will
|
||||
* return TagFailure is no file is currently open, TagSuccess otherwise.
|
||||
*/
|
||||
extern tagResult tagsClose (tagFile *const file);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* $Id: rexx.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2001-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for the REXX language
|
||||
* (http://www.rexxla.org, http://www2.hursley.ibm.com/rexx).
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* always include first */
|
||||
#include "parse.h" /* always include */
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installRexxRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language, "^([A-Za-z0-9@#$\\.!?_]+)[ \t]*:",
|
||||
"\\1", "s,subroutine,subroutines", NULL);
|
||||
}
|
||||
|
||||
extern parserDefinition* RexxParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "cmd", "rexx", "rx", NULL };
|
||||
parserDefinition* const def = parserNew ("REXX");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installRexxRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,881 @@
|
||||
/*
|
||||
* $Id: routines.c,v 1.22 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains a lose assortment of shared functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h> /* to declare malloc (), realloc () */
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h> /* to declare tempnam(), and SEEK_SET (hopefully) */
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h> /* to declar O_RDWR, O_CREAT, O_EXCL */
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h> /* to declare mkstemp () */
|
||||
#endif
|
||||
|
||||
/* To declare "struct stat" and stat ().
|
||||
*/
|
||||
#if defined (HAVE_SYS_TYPES_H)
|
||||
# include <sys/types.h>
|
||||
#else
|
||||
# if defined (HAVE_TYPES_H)
|
||||
# include <types.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
# include <sys/stat.h>
|
||||
#else
|
||||
# ifdef HAVE_STAT_H
|
||||
# include <stat.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DOS_H
|
||||
# include <dos.h> /* to declare MAXPATH */
|
||||
#endif
|
||||
#ifdef HAVE_DIRECT_H
|
||||
# include <direct.h> /* to _getcwd */
|
||||
#endif
|
||||
#ifdef HAVE_DIR_H
|
||||
# include <dir.h> /* to declare findfirst() and findnext() */
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h> /* to declare open() */
|
||||
#endif
|
||||
#include "debug.h"
|
||||
#include "routines.h"
|
||||
|
||||
#ifdef TRAP_MEMORY_CALLS
|
||||
# include "safe_malloc.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#ifndef TMPDIR
|
||||
# define TMPDIR "/tmp"
|
||||
#endif
|
||||
|
||||
/* File type tests.
|
||||
*/
|
||||
#ifndef S_ISREG
|
||||
# if defined (S_IFREG) && ! defined (AMIGA)
|
||||
# define S_ISREG(mode) ((mode) & S_IFREG)
|
||||
# else
|
||||
# define S_ISREG(mode) TRUE /* assume regular file */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef S_ISLNK
|
||||
# ifdef S_IFLNK
|
||||
# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
|
||||
# else
|
||||
# define S_ISLNK(mode) FALSE /* assume no soft links */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
# ifdef S_IFDIR
|
||||
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
# else
|
||||
# define S_ISDIR(mode) FALSE /* assume no soft links */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef S_IFMT
|
||||
# define S_IFMT 0
|
||||
#endif
|
||||
|
||||
#ifndef S_IXUSR
|
||||
# define S_IXUSR 0
|
||||
#endif
|
||||
#ifndef S_IXGRP
|
||||
# define S_IXGRP 0
|
||||
#endif
|
||||
#ifndef S_IXOTH
|
||||
# define S_IXOTH 0
|
||||
#endif
|
||||
|
||||
#ifndef S_IRUSR
|
||||
# define S_IRUSR 0400
|
||||
#endif
|
||||
#ifndef S_IWUSR
|
||||
# define S_IWUSR 0200
|
||||
#endif
|
||||
|
||||
#ifndef S_ISUID
|
||||
# define S_ISUID 0
|
||||
#endif
|
||||
|
||||
/* Hack for rediculous practice of Microsoft Visual C++.
|
||||
*/
|
||||
#if defined (WIN32)
|
||||
# if defined (_MSC_VER)
|
||||
# define stat _stat
|
||||
# define getcwd _getcwd
|
||||
# define currentdrive() (_getdrive() + 'A' - 1)
|
||||
# define PATH_MAX _MAX_PATH
|
||||
# elif defined (__BORLANDC__)
|
||||
# define PATH_MAX MAXPATH
|
||||
# define currentdrive() (getdisk() + 'A')
|
||||
# elif defined (DJGPP)
|
||||
# define currentdrive() (getdisk() + 'A')
|
||||
# else
|
||||
# define currentdrive() 'C'
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX 256
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Miscellaneous macros
|
||||
*/
|
||||
#define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
#if defined (MSDOS_STYLE_PATH)
|
||||
const char *const PathDelimiters = ":/\\";
|
||||
#elif defined (VMS)
|
||||
const char *const PathDelimiters = ":]>";
|
||||
#endif
|
||||
|
||||
char *CurrentDirectory;
|
||||
|
||||
static const char *ExecutableProgram;
|
||||
static const char *ExecutableName;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
#ifdef NEED_PROTO_STAT
|
||||
extern int stat (const char *, struct stat *);
|
||||
#endif
|
||||
#ifdef NEED_PROTO_LSTAT
|
||||
extern int lstat (const char *, struct stat *);
|
||||
#endif
|
||||
#if defined (MSDOS) || defined (WIN32) || defined (VMS) || defined (__EMX__) || defined (AMIGA)
|
||||
# define lstat(fn,buf) stat(fn,buf)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern void freeRoutineResources (void)
|
||||
{
|
||||
if (CurrentDirectory != NULL)
|
||||
eFree (CurrentDirectory);
|
||||
}
|
||||
|
||||
extern void setExecutableName (const char *const path)
|
||||
{
|
||||
ExecutableProgram = path;
|
||||
ExecutableName = baseFilename (path);
|
||||
#ifdef VAXC
|
||||
{
|
||||
/* remove filetype from executable name */
|
||||
char *p = strrchr (ExecutableName, '.');
|
||||
if (p != NULL)
|
||||
*p = '\0';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern const char *getExecutableName (void)
|
||||
{
|
||||
return ExecutableName;
|
||||
}
|
||||
|
||||
extern void error (
|
||||
const errorSelection selection, const char *const format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, format);
|
||||
fprintf (errout, "%s: %s", getExecutableName (),
|
||||
selected (selection, WARNING) ? "Warning: " : "");
|
||||
vfprintf (errout, format, ap);
|
||||
if (selected (selection, PERROR))
|
||||
#ifdef HAVE_STRERROR
|
||||
fprintf (errout, " : %s", strerror (errno));
|
||||
#else
|
||||
perror (" ");
|
||||
#endif
|
||||
fputs ("\n", errout);
|
||||
va_end (ap);
|
||||
if (selected (selection, FATAL))
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory allocation functions
|
||||
*/
|
||||
|
||||
extern void *eMalloc (const size_t size)
|
||||
{
|
||||
void *buffer = malloc (size);
|
||||
|
||||
if (buffer == NULL)
|
||||
error (FATAL, "out of memory");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
extern void *eCalloc (const size_t count, const size_t size)
|
||||
{
|
||||
void *buffer = calloc (count, size);
|
||||
|
||||
if (buffer == NULL)
|
||||
error (FATAL, "out of memory");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
extern void *eRealloc (void *const ptr, const size_t size)
|
||||
{
|
||||
void *buffer;
|
||||
if (ptr == NULL)
|
||||
buffer = eMalloc (size);
|
||||
else
|
||||
{
|
||||
buffer = realloc (ptr, size);
|
||||
if (buffer == NULL)
|
||||
error (FATAL, "out of memory");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
extern void eFree (void *const ptr)
|
||||
{
|
||||
Assert (ptr != NULL);
|
||||
free (ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* String manipulation functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compare two strings, ignoring case.
|
||||
* Return 0 for match, < 0 for smaller, > 0 for bigger
|
||||
* Make sure case is folded to uppercase in comparison (like for 'sort -f')
|
||||
* This makes a difference when one of the chars lies between upper and lower
|
||||
* ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
|
||||
*/
|
||||
extern int struppercmp (const char *s1, const char *s2)
|
||||
{
|
||||
int result;
|
||||
do
|
||||
{
|
||||
result = toupper ((int) *s1) - toupper ((int) *s2);
|
||||
} while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
extern int strnuppercmp (const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
int result;
|
||||
do
|
||||
{
|
||||
result = toupper ((int) *s1) - toupper ((int) *s2);
|
||||
} while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef HAVE_STRSTR
|
||||
extern char* strstr (const char *str, const char *substr)
|
||||
{
|
||||
const size_t length = strlen (substr);
|
||||
const char *match = NULL;
|
||||
const char *p;
|
||||
|
||||
for (p = str ; *p != '\0' && match == NULL ; ++p)
|
||||
if (strncmp (p, substr, length) == 0)
|
||||
match = p;
|
||||
return (char*) match;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern char* eStrdup (const char* str)
|
||||
{
|
||||
char* result = xMalloc (strlen (str) + 1, char);
|
||||
strcpy (result, str);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void toLowerString (char* str)
|
||||
{
|
||||
while (*str != '\0')
|
||||
{
|
||||
*str = tolower ((int) *str);
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
extern void toUpperString (char* str)
|
||||
{
|
||||
while (*str != '\0')
|
||||
{
|
||||
*str = toupper ((int) *str);
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
/* Newly allocated string containing lower case conversion of a string.
|
||||
*/
|
||||
extern char* newLowerString (const char* str)
|
||||
{
|
||||
char* const result = xMalloc (strlen (str) + 1, char);
|
||||
int i = 0;
|
||||
do
|
||||
result [i] = tolower ((int) str [i]);
|
||||
while (str [i++] != '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Newly allocated string containing upper case conversion of a string.
|
||||
*/
|
||||
extern char* newUpperString (const char* str)
|
||||
{
|
||||
char* const result = xMalloc (strlen (str) + 1, char);
|
||||
int i = 0;
|
||||
do
|
||||
result [i] = toupper ((int) str [i]);
|
||||
while (str [i++] != '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* File system functions
|
||||
*/
|
||||
|
||||
extern void setCurrentDirectory (void)
|
||||
{
|
||||
#ifndef AMIGA
|
||||
char* buf;
|
||||
#endif
|
||||
if (CurrentDirectory == NULL)
|
||||
CurrentDirectory = xMalloc ((size_t) (PATH_MAX + 1), char);
|
||||
#ifdef AMIGA
|
||||
strcpy (CurrentDirectory, ".");
|
||||
#else
|
||||
buf = getcwd (CurrentDirectory, PATH_MAX);
|
||||
if (buf == NULL)
|
||||
perror ("");
|
||||
#endif
|
||||
if (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1] !=
|
||||
PATH_SEPARATOR)
|
||||
{
|
||||
sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c",
|
||||
OUTPUT_PATH_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AMIGA
|
||||
static boolean isAmigaDirectory (const char *const name)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
struct FileInfoBlock *const fib = xMalloc (1, struct FileInfoBlock);
|
||||
if (fib != NULL)
|
||||
{
|
||||
const BPTR flock = Lock ((UBYTE *) name, (long) ACCESS_READ);
|
||||
|
||||
if (flock != (BPTR) NULL)
|
||||
{
|
||||
if (Examine (flock, fib))
|
||||
result = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
|
||||
UnLock (flock);
|
||||
}
|
||||
eFree (fib);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* For caching of stat() calls */
|
||||
extern fileStatus *eStat (const char *const fileName)
|
||||
{
|
||||
struct stat status;
|
||||
static fileStatus file;
|
||||
if (file.name == NULL || strcmp (fileName, file.name) != 0)
|
||||
{
|
||||
if (file.name != NULL)
|
||||
eFree (file.name);
|
||||
file.name = eStrdup (fileName);
|
||||
if (lstat (file.name, &status) != 0)
|
||||
file.exists = FALSE;
|
||||
else
|
||||
{
|
||||
file.isSymbolicLink = (boolean) S_ISLNK (status.st_mode);
|
||||
if (file.isSymbolicLink && stat (file.name, &status) != 0)
|
||||
file.exists = FALSE;
|
||||
else
|
||||
{
|
||||
file.exists = TRUE;
|
||||
#ifdef AMIGA
|
||||
file.isDirectory = isAmigaDirectory (file.name);
|
||||
#else
|
||||
file.isDirectory = (boolean) S_ISDIR (status.st_mode);
|
||||
#endif
|
||||
file.isNormalFile = (boolean) (S_ISREG (status.st_mode));
|
||||
file.isExecutable = (boolean) ((status.st_mode &
|
||||
(S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
|
||||
file.isSetuid = (boolean) ((status.st_mode & S_ISUID) != 0);
|
||||
file.size = status.st_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
return &file;
|
||||
}
|
||||
|
||||
extern boolean doesFileExist (const char *const fileName)
|
||||
{
|
||||
fileStatus *status = eStat (fileName);
|
||||
return status->exists;
|
||||
}
|
||||
|
||||
extern boolean isRecursiveLink (const char* const dirName)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
fileStatus *status = eStat (dirName);
|
||||
if (status->isSymbolicLink)
|
||||
{
|
||||
char* const path = absoluteFilename (dirName);
|
||||
while (path [strlen (path) - 1] == PATH_SEPARATOR)
|
||||
path [strlen (path) - 1] = '\0';
|
||||
while (! result && strlen (path) > (size_t) 1)
|
||||
{
|
||||
char *const separator = strrchr (path, PATH_SEPARATOR);
|
||||
if (separator == NULL)
|
||||
break;
|
||||
else if (separator == path) /* backed up to root directory */
|
||||
*(separator + 1) = '\0';
|
||||
else
|
||||
*separator = '\0';
|
||||
result = isSameFile (path, dirName);
|
||||
}
|
||||
eFree (path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef HAVE_FGETPOS
|
||||
|
||||
extern int fgetpos (FILE *stream, fpos_t *pos)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
*pos = ftell (stream);
|
||||
if (*pos == -1L)
|
||||
result = -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
extern int fsetpos (FILE *stream, fpos_t const *pos)
|
||||
{
|
||||
return fseek (stream, *pos, SEEK_SET);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pathname manipulation (O/S dependent!!!)
|
||||
*/
|
||||
|
||||
static boolean isPathSeparator (const int c)
|
||||
{
|
||||
boolean result;
|
||||
#if defined (MSDOS_STYLE_PATH) || defined (VMS)
|
||||
result = (boolean) (strchr (PathDelimiters, c) != NULL);
|
||||
#else
|
||||
result = (boolean) (c == PATH_SEPARATOR);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ! defined (HAVE_STAT_ST_INO)
|
||||
|
||||
static void canonicalizePath (char *const path __unused__)
|
||||
{
|
||||
#if defined (MSDOS_STYLE_PATH)
|
||||
char *p;
|
||||
for (p = path ; *p != '\0' ; ++p)
|
||||
if (isPathSeparator (*p) && *p != ':')
|
||||
*p = PATH_SEPARATOR;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern boolean isSameFile (const char *const name1, const char *const name2)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
#if defined (HAVE_STAT_ST_INO)
|
||||
struct stat stat1, stat2;
|
||||
|
||||
if (stat (name1, &stat1) == 0 && stat (name2, &stat2) == 0)
|
||||
result = (boolean) (stat1.st_ino == stat2.st_ino);
|
||||
#else
|
||||
{
|
||||
char *const n1 = absoluteFilename (name1);
|
||||
char *const n2 = absoluteFilename (name2);
|
||||
canonicalizePath (n1);
|
||||
canonicalizePath (n2);
|
||||
# if defined (CASE_INSENSITIVE_FILENAMES)
|
||||
result = (boolean) (strcasecmp (n1, n2) == 0);
|
||||
#else
|
||||
result = (boolean) (strcmp (n1, n2) == 0);
|
||||
#endif
|
||||
free (n1);
|
||||
free (n2);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
extern const char *baseFilename (const char *const filePath)
|
||||
{
|
||||
#if defined (MSDOS_STYLE_PATH) || defined (VMS)
|
||||
const char *tail = NULL;
|
||||
unsigned int i;
|
||||
|
||||
/* Find whichever of the path delimiters is last.
|
||||
*/
|
||||
for (i = 0 ; i < strlen (PathDelimiters) ; ++i)
|
||||
{
|
||||
const char *sep = strrchr (filePath, PathDelimiters [i]);
|
||||
|
||||
if (sep > tail)
|
||||
tail = sep;
|
||||
}
|
||||
#else
|
||||
const char *tail = strrchr (filePath, PATH_SEPARATOR);
|
||||
#endif
|
||||
if (tail == NULL)
|
||||
tail = filePath;
|
||||
else
|
||||
++tail; /* step past last delimiter */
|
||||
#ifdef VAXC
|
||||
{
|
||||
/* remove version number from filename */
|
||||
char *p = strrchr ((char *) tail, ';');
|
||||
if (p != NULL)
|
||||
*p = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
||||
extern const char *fileExtension (const char *const fileName)
|
||||
{
|
||||
const char *extension;
|
||||
const char *pDelimiter = NULL;
|
||||
const char *const base = baseFilename (fileName);
|
||||
#ifdef QDOS
|
||||
pDelimiter = strrchr (base, '_');
|
||||
#endif
|
||||
if (pDelimiter == NULL)
|
||||
pDelimiter = strrchr (base, '.');
|
||||
|
||||
if (pDelimiter == NULL)
|
||||
extension = "";
|
||||
else
|
||||
extension = pDelimiter + 1; /* skip to first char of extension */
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
extern boolean isAbsolutePath (const char *const path)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
#if defined (MSDOS_STYLE_PATH)
|
||||
if (isPathSeparator (path [0]))
|
||||
result = TRUE;
|
||||
else if (isalpha (path [0]) && path [1] == ':')
|
||||
{
|
||||
if (isPathSeparator (path [2]))
|
||||
result = TRUE;
|
||||
else
|
||||
/* We don't support non-absolute file names with a drive
|
||||
* letter, like `d:NAME' (it's too much hassle).
|
||||
*/
|
||||
error (FATAL,
|
||||
"%s: relative file names with drive letters not supported",
|
||||
path);
|
||||
}
|
||||
#elif defined (VMS)
|
||||
result = (boolean) (strchr (path, ':') != NULL);
|
||||
#else
|
||||
result = isPathSeparator (path [0]);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
extern vString *combinePathAndFile (
|
||||
const char *const path, const char *const file)
|
||||
{
|
||||
vString *const filePath = vStringNew ();
|
||||
#ifdef VMS
|
||||
const char *const directoryId = strstr (file, ".DIR;1");
|
||||
|
||||
if (directoryId == NULL)
|
||||
{
|
||||
const char *const versionId = strchr (file, ';');
|
||||
|
||||
vStringCopyS (filePath, path);
|
||||
if (versionId == NULL)
|
||||
vStringCatS (filePath, file);
|
||||
else
|
||||
vStringNCatS (filePath, file, versionId - file);
|
||||
vStringCopyToLower (filePath, filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* File really is a directory; append it to the path.
|
||||
* Gotcha: doesn't work with logical names.
|
||||
*/
|
||||
vStringNCopyS (filePath, path, strlen (path) - 1);
|
||||
vStringPut (filePath, '.');
|
||||
vStringNCatS (filePath, file, directoryId - file);
|
||||
if (strchr (path, '[') != NULL)
|
||||
vStringPut (filePath, ']');
|
||||
else
|
||||
vStringPut (filePath, '>');
|
||||
vStringTerminate (filePath);
|
||||
}
|
||||
#else
|
||||
const int lastChar = path [strlen (path) - 1];
|
||||
boolean terminated = isPathSeparator (lastChar);
|
||||
|
||||
vStringCopyS (filePath, path);
|
||||
if (! terminated)
|
||||
{
|
||||
vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
|
||||
vStringTerminate (filePath);
|
||||
}
|
||||
vStringCatS (filePath, file);
|
||||
#endif
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/* Return a newly-allocated string whose contents concatenate those of
|
||||
* s1, s2, s3.
|
||||
* Routine adapted from Gnu etags.
|
||||
*/
|
||||
static char* concat (const char *s1, const char *s2, const char *s3)
|
||||
{
|
||||
int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
|
||||
char *result = xMalloc (len1 + len2 + len3 + 1, char);
|
||||
|
||||
strcpy (result, s1);
|
||||
strcpy (result + len1, s2);
|
||||
strcpy (result + len1 + len2, s3);
|
||||
result [len1 + len2 + len3] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return a newly allocated string containing the absolute file name of FILE
|
||||
* given CWD (which should end with a slash).
|
||||
* Routine adapted from Gnu etags.
|
||||
*/
|
||||
extern char* absoluteFilename (const char *file)
|
||||
{
|
||||
char *slashp, *cp;
|
||||
char *res = NULL;
|
||||
if (isAbsolutePath (file))
|
||||
{
|
||||
#ifdef MSDOS_STYLE_PATH
|
||||
if (file [1] == ':')
|
||||
res = eStrdup (file);
|
||||
else
|
||||
{
|
||||
char drive [3];
|
||||
sprintf (drive, "%c:", currentdrive ());
|
||||
res = concat (drive, file, "");
|
||||
}
|
||||
#else
|
||||
res = eStrdup (file);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
res = concat (CurrentDirectory, file, "");
|
||||
|
||||
/* Delete the "/dirname/.." and "/." substrings. */
|
||||
slashp = strchr (res, PATH_SEPARATOR);
|
||||
while (slashp != NULL && slashp [0] != '\0')
|
||||
{
|
||||
if (slashp[1] == '.')
|
||||
{
|
||||
if (slashp [2] == '.' &&
|
||||
(slashp [3] == PATH_SEPARATOR || slashp [3] == '\0'))
|
||||
{
|
||||
cp = slashp;
|
||||
do
|
||||
cp--;
|
||||
while (cp >= res && ! isAbsolutePath (cp));
|
||||
if (cp < res)
|
||||
cp = slashp;/* the absolute name begins with "/.." */
|
||||
#ifdef MSDOS_STYLE_PATH
|
||||
/* Under MSDOS and NT we get `d:/NAME' as absolute file name,
|
||||
* so the luser could say `d:/../NAME'. We silently treat this
|
||||
* as `d:/NAME'.
|
||||
*/
|
||||
else if (cp [0] != PATH_SEPARATOR)
|
||||
cp = slashp;
|
||||
#endif
|
||||
strcpy (cp, slashp + 3);
|
||||
slashp = cp;
|
||||
continue;
|
||||
}
|
||||
else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0')
|
||||
{
|
||||
strcpy (slashp, slashp + 2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
slashp = strchr (slashp + 1, PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
if (res [0] == '\0')
|
||||
return eStrdup ("/");
|
||||
else
|
||||
{
|
||||
#ifdef MSDOS_STYLE_PATH
|
||||
/* Canonicalize drive letter case. */
|
||||
if (res [1] == ':' && islower (res [0]))
|
||||
res [0] = toupper (res [0]);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a newly allocated string containing the absolute file name of dir
|
||||
* where `file' resides given `CurrentDirectory'.
|
||||
* Routine adapted from Gnu etags.
|
||||
*/
|
||||
extern char* absoluteDirname (char *file)
|
||||
{
|
||||
char *slashp, *res;
|
||||
char save;
|
||||
slashp = strrchr (file, PATH_SEPARATOR);
|
||||
if (slashp == NULL)
|
||||
res = eStrdup (CurrentDirectory);
|
||||
else
|
||||
{
|
||||
save = slashp [1];
|
||||
slashp [1] = '\0';
|
||||
res = absoluteFilename (file);
|
||||
slashp [1] = save;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Return a newly allocated string containing the file name of FILE relative
|
||||
* to the absolute directory DIR (which should end with a slash).
|
||||
* Routine adapted from Gnu etags.
|
||||
*/
|
||||
extern char* relativeFilename (const char *file, const char *dir)
|
||||
{
|
||||
const char *fp, *dp;
|
||||
char *absdir, *res;
|
||||
int i;
|
||||
|
||||
/* Find the common root of file and dir (with a trailing slash). */
|
||||
absdir = absoluteFilename (file);
|
||||
fp = absdir;
|
||||
dp = dir;
|
||||
while (*fp++ == *dp++)
|
||||
continue;
|
||||
fp--;
|
||||
dp--; /* back to the first differing char */
|
||||
do
|
||||
{ /* look at the equal chars until path sep */
|
||||
if (fp == absdir)
|
||||
return absdir; /* first char differs, give up */
|
||||
fp--;
|
||||
dp--;
|
||||
} while (*fp != PATH_SEPARATOR);
|
||||
|
||||
/* Build a sequence of "../" strings for the resulting relative file name.
|
||||
*/
|
||||
i = 0;
|
||||
while ((dp = strchr (dp + 1, PATH_SEPARATOR)) != NULL)
|
||||
i += 1;
|
||||
res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
|
||||
res [0] = '\0';
|
||||
while (i-- > 0)
|
||||
strcat (res, "../");
|
||||
|
||||
/* Add the file name relative to the common root of file and dir. */
|
||||
strcat (res, fp + 1);
|
||||
free (absdir);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
extern FILE *tempFile (const char *const mode, char **const pName)
|
||||
{
|
||||
char *name;
|
||||
FILE *fp;
|
||||
int fd;
|
||||
#if defined(HAVE_MKSTEMP)
|
||||
const char *const pattern = "tags.XXXXXX";
|
||||
const char *tmpdir = NULL;
|
||||
fileStatus *file = eStat (ExecutableProgram);
|
||||
if (! file->isSetuid)
|
||||
tmpdir = getenv ("TMPDIR");
|
||||
if (tmpdir == NULL)
|
||||
tmpdir = TMPDIR;
|
||||
name = xMalloc (strlen (tmpdir) + 1 + strlen (pattern) + 1, char);
|
||||
sprintf (name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, pattern);
|
||||
fd = mkstemp (name);
|
||||
#elif defined(HAVE_TEMPNAM)
|
||||
name = tempnam (TMPDIR, "tags");
|
||||
if (name == NULL)
|
||||
error (FATAL | PERROR, "cannot allocate temporary file name");
|
||||
fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
||||
#else
|
||||
name = xMalloc (L_tmpnam, char);
|
||||
if (tmpnam (name) != name)
|
||||
error (FATAL | PERROR, "cannot assign temporary file name");
|
||||
fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
||||
#endif
|
||||
if (fd == -1)
|
||||
error (FATAL | PERROR, "cannot open temporary file");
|
||||
fp = fdopen (fd, mode);
|
||||
if (fp == NULL)
|
||||
error (FATAL | PERROR, "cannot open temporary file");
|
||||
DebugStatement (
|
||||
debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
|
||||
Assert (*pName == NULL);
|
||||
*pName = name;
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* $Id: routines.h,v 1.11 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to routines.c
|
||||
*/
|
||||
#ifndef _ROUTINES_H
|
||||
#define _ROUTINES_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define xMalloc(n,Type) (Type *)eMalloc((size_t)(n) * sizeof (Type))
|
||||
#define xCalloc(n,Type) (Type *)eCalloc((size_t)(n), sizeof (Type))
|
||||
#define xRealloc(p,n,Type) (Type *)eRealloc((p), (n) * sizeof (Type))
|
||||
|
||||
/*
|
||||
* Portability macros
|
||||
*/
|
||||
#ifndef PATH_SEPARATOR
|
||||
# if defined (MSDOS_STYLE_PATH)
|
||||
# define PATH_SEPARATOR '\\'
|
||||
# elif defined (QDOS)
|
||||
# define PATH_SEPARATOR '_'
|
||||
# else
|
||||
# define PATH_SEPARATOR '/'
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (MSDOS_STYLE_PATH) && defined (UNIX_PATH_SEPARATOR)
|
||||
# define OUTPUT_PATH_SEPARATOR '/'
|
||||
#else
|
||||
# define OUTPUT_PATH_SEPARATOR PATH_SEPARATOR
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
#if defined (MSDOS_STYLE_PATH) || defined (VMS)
|
||||
extern const char *const PathDelimiters;
|
||||
#endif
|
||||
extern char *CurrentDirectory;
|
||||
typedef int errorSelection;
|
||||
enum eErrorTypes { FATAL = 1, WARNING = 2, PERROR = 4 };
|
||||
|
||||
typedef struct {
|
||||
/* Name of file for which status is valid */
|
||||
char* name;
|
||||
|
||||
/* Does file exist? If not, members below do not contain valid data. */
|
||||
boolean exists;
|
||||
|
||||
/* is file path a symbolic link to another file? */
|
||||
boolean isSymbolicLink;
|
||||
|
||||
/* Is file (pointed to) a directory? */
|
||||
boolean isDirectory;
|
||||
|
||||
/* Is file (pointed to) a normal file? */
|
||||
boolean isNormalFile;
|
||||
|
||||
/* Is file (pointed to) executable? */
|
||||
boolean isExecutable;
|
||||
|
||||
/* Is file (pointed to) setuid? */
|
||||
boolean isSetuid;
|
||||
|
||||
/* Size of file (pointed to) */
|
||||
unsigned long size;
|
||||
} fileStatus;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void freeRoutineResources (void);
|
||||
extern void setExecutableName (const char *const path);
|
||||
extern const char *getExecutableName (void);
|
||||
extern void error (const errorSelection selection, const char *const format, ...) __printf__ (2, 3);
|
||||
|
||||
/* Memory allocation functions */
|
||||
#ifdef NEED_PROTO_MALLOC
|
||||
extern void *malloc (size_t);
|
||||
extern void *realloc (void *ptr, size_t);
|
||||
#endif
|
||||
extern void *eMalloc (const size_t size);
|
||||
extern void *eCalloc (const size_t count, const size_t size);
|
||||
extern void *eRealloc (void *const ptr, const size_t size);
|
||||
extern void eFree (void *const ptr);
|
||||
|
||||
/* String manipulation functions */
|
||||
extern int struppercmp (const char *s1, const char *s2);
|
||||
extern int strnuppercmp (const char *s1, const char *s2, size_t n);
|
||||
#ifndef HAVE_STRSTR
|
||||
extern char* strstr (const char *str, const char *substr);
|
||||
#endif
|
||||
extern char* eStrdup (const char* str);
|
||||
extern void toLowerString (char* str);
|
||||
extern void toUpperString (char* str);
|
||||
extern char* newLowerString (const char* str);
|
||||
extern char* newUpperString (const char* str);
|
||||
|
||||
/* File system functions */
|
||||
extern void setCurrentDirectory (void);
|
||||
extern fileStatus *eStat (const char *const fileName);
|
||||
extern boolean doesFileExist (const char *const fileName);
|
||||
extern boolean isRecursiveLink (const char* const dirName);
|
||||
extern boolean isSameFile (const char *const name1, const char *const name2);
|
||||
#if defined(NEED_PROTO_FGETPOS)
|
||||
extern int fgetpos (FILE *stream, fpos_t *pos);
|
||||
extern int fsetpos (FILE *stream, fpos_t *pos);
|
||||
#endif
|
||||
extern const char *baseFilename (const char *const filePath);
|
||||
extern const char *fileExtension (const char *const fileName);
|
||||
extern boolean isAbsolutePath (const char *const path);
|
||||
extern vString *combinePathAndFile (const char *const path, const char *const file);
|
||||
extern char* absoluteFilename (const char *file);
|
||||
extern char* absoluteDirname (char *file);
|
||||
extern char* relativeFilename (const char *file, const char *dir);
|
||||
extern FILE *tempFile (const char *const mode, char **const pName);
|
||||
|
||||
#endif /* _ROUTINES_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* $Id: ruby.c,v 1.6 2006/05/30 04:37:12 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2001, Thaddeus Covert <sahuagin@mediaone.net>
|
||||
* Copyright (c) 2002 Matthias Veit <matthias_veit@yahoo.de>
|
||||
* Copyright (c) 2004 Elliott Hughes <enh@acm.org>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for Ruby language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "entry.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_UNDEFINED = -1, K_CLASS, K_METHOD, K_MODULE, K_SINGLETON
|
||||
} rubyKind;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static kindOption RubyKinds [] = {
|
||||
{ TRUE, 'c', "class", "classes" },
|
||||
{ TRUE, 'f', "method", "methods" },
|
||||
{ TRUE, 'm', "module", "modules" },
|
||||
{ TRUE, 'F', "singleton method", "singleton methods" }
|
||||
};
|
||||
|
||||
static stringList* nesting = 0;
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Returns a string describing the scope in 'list'.
|
||||
* We record the current scope as a list of entered scopes.
|
||||
* Scopes corresponding to 'if' statements and the like are
|
||||
* represented by empty strings. Scopes corresponding to
|
||||
* modules and classes are represented by the name of the
|
||||
* module or class.
|
||||
*/
|
||||
static vString* stringListToScope (const stringList* list)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int chunks_output = 0;
|
||||
vString* result = vStringNew ();
|
||||
const unsigned int max = stringListCount (list);
|
||||
for (i = 0; i < max; ++i)
|
||||
{
|
||||
vString* chunk = stringListItem (list, i);
|
||||
if (vStringLength (chunk) > 0)
|
||||
{
|
||||
vStringCatS (result, (chunks_output++ > 0) ? "." : "");
|
||||
vStringCatS (result, vStringValue (chunk));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to advance 's' past 'literal'.
|
||||
* Returns TRUE if it did, FALSE (and leaves 's' where
|
||||
* it was) otherwise.
|
||||
*/
|
||||
static boolean canMatch (const unsigned char** s, const char* literal)
|
||||
{
|
||||
const int literal_length = strlen (literal);
|
||||
const unsigned char next_char = *(*s + literal_length);
|
||||
if (strncmp ((const char*) *s, literal, literal_length) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* Additionally check that we're at the end of a token. */
|
||||
if ( ! (next_char == 0 || isspace (next_char) || next_char == '('))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*s += literal_length;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to advance 'cp' past a Ruby operator method name. Returns
|
||||
* TRUE if successful (and copies the name into 'name'), FALSE otherwise.
|
||||
*/
|
||||
static boolean parseRubyOperator (vString* name, const unsigned char** cp)
|
||||
{
|
||||
static const char* RUBY_OPERATORS[] = {
|
||||
"[]", "[]=",
|
||||
"**",
|
||||
"!", "~", "+@", "-@",
|
||||
"*", "/", "%",
|
||||
"+", "-",
|
||||
">>", "<<",
|
||||
"&",
|
||||
"^", "|",
|
||||
"<=", "<", ">", ">=",
|
||||
"<=>", "==", "===", "!=", "=~", "!~",
|
||||
0
|
||||
};
|
||||
int i;
|
||||
for (i = 0; RUBY_OPERATORS[i] != 0; ++i)
|
||||
{
|
||||
if (canMatch (cp, RUBY_OPERATORS[i]))
|
||||
{
|
||||
vStringCatS (name, RUBY_OPERATORS[i]);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emits a tag for the given 'name' of kind 'kind' at the current nesting.
|
||||
*/
|
||||
static void emitRubyTag (vString* name, rubyKind kind)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
vString* scope;
|
||||
|
||||
vStringTerminate (name);
|
||||
scope = stringListToScope (nesting);
|
||||
|
||||
initTagEntry (&tag, vStringValue (name));
|
||||
if (vStringLength (scope) > 0) {
|
||||
tag.extensionFields.scope [0] = "class";
|
||||
tag.extensionFields.scope [1] = vStringValue (scope);
|
||||
}
|
||||
tag.kindName = RubyKinds [kind].name;
|
||||
tag.kind = RubyKinds [kind].letter;
|
||||
makeTagEntry (&tag);
|
||||
|
||||
stringListAdd (nesting, vStringNewCopy (name));
|
||||
|
||||
vStringClear (name);
|
||||
vStringDelete (scope);
|
||||
}
|
||||
|
||||
/* Tests whether 'ch' is a character in 'list'. */
|
||||
static boolean charIsIn (char ch, const char* list)
|
||||
{
|
||||
return (strchr (list, ch) != 0);
|
||||
}
|
||||
|
||||
/* Advances 'cp' over leading whitespace. */
|
||||
static void skipWhitespace (const unsigned char** cp)
|
||||
{
|
||||
while (isspace (**cp))
|
||||
{
|
||||
++*cp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies the characters forming an identifier from *cp into
|
||||
* name, leaving *cp pointing to the character after the identifier.
|
||||
*/
|
||||
static rubyKind parseIdentifier (
|
||||
const unsigned char** cp, vString* name, rubyKind kind)
|
||||
{
|
||||
/* Method names are slightly different to class and variable names.
|
||||
* A method name may optionally end with a question mark, exclamation
|
||||
* point or equals sign. These are all part of the name.
|
||||
* A method name may also contain a period if it's a singleton method.
|
||||
*/
|
||||
const char* also_ok = (kind == K_METHOD) ? "_.?!=" : "_";
|
||||
|
||||
skipWhitespace (cp);
|
||||
|
||||
/* Check for an anonymous (singleton) class such as "class << HTTP". */
|
||||
if (kind == K_CLASS && **cp == '<' && *(*cp + 1) == '<')
|
||||
{
|
||||
return K_UNDEFINED;
|
||||
}
|
||||
|
||||
/* Check for operators such as "def []=(key, val)". */
|
||||
if (kind == K_METHOD || kind == K_SINGLETON)
|
||||
{
|
||||
if (parseRubyOperator (name, cp))
|
||||
{
|
||||
return kind;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy the identifier into 'name'. */
|
||||
while (**cp != 0 && (isalnum (**cp) || charIsIn (**cp, also_ok)))
|
||||
{
|
||||
char last_char = **cp;
|
||||
|
||||
vStringPut (name, last_char);
|
||||
++*cp;
|
||||
|
||||
if (kind == K_METHOD)
|
||||
{
|
||||
/* Recognize singleton methods. */
|
||||
if (last_char == '.')
|
||||
{
|
||||
vStringTerminate (name);
|
||||
vStringClear (name);
|
||||
return parseIdentifier (cp, name, K_SINGLETON);
|
||||
}
|
||||
|
||||
/* Recognize characters which mark the end of a method name. */
|
||||
if (charIsIn (last_char, "?!="))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind)
|
||||
{
|
||||
if (isspace (**cp))
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
rubyKind actual_kind = parseIdentifier (cp, name, expected_kind);
|
||||
|
||||
if (actual_kind == K_UNDEFINED || vStringLength (name) == 0)
|
||||
{
|
||||
/*
|
||||
* What kind of tags should we create for code like this?
|
||||
*
|
||||
* %w(self.clfloor clfloor).each do |name|
|
||||
* module_eval <<-"end;"
|
||||
* def #{name}(x, y=1)
|
||||
* q, r = x.divmod(y)
|
||||
* q = q.to_i
|
||||
* return q, r
|
||||
* end
|
||||
* end;
|
||||
* end
|
||||
*
|
||||
* Or this?
|
||||
*
|
||||
* class << HTTP
|
||||
*
|
||||
* For now, we don't create any.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
emitRubyTag (name, actual_kind);
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
}
|
||||
|
||||
static void enterUnnamedScope (void)
|
||||
{
|
||||
stringListAdd (nesting, vStringNewInit (""));
|
||||
}
|
||||
|
||||
static void findRubyTags (void)
|
||||
{
|
||||
const unsigned char *line;
|
||||
boolean inMultiLineComment = FALSE;
|
||||
|
||||
nesting = stringListNew ();
|
||||
|
||||
/* FIXME: this whole scheme is wrong, because Ruby isn't line-based.
|
||||
* You could perfectly well write:
|
||||
*
|
||||
* def
|
||||
* method
|
||||
* puts("hello")
|
||||
* end
|
||||
*
|
||||
* if you wished, and this function would fail to recognize anything.
|
||||
*/
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
|
||||
if (canMatch (&cp, "=begin"))
|
||||
{
|
||||
inMultiLineComment = TRUE;
|
||||
continue;
|
||||
}
|
||||
if (canMatch (&cp, "=end"))
|
||||
{
|
||||
inMultiLineComment = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
skipWhitespace (&cp);
|
||||
|
||||
/* Avoid mistakenly starting a scope for modifiers such as
|
||||
*
|
||||
* return if <exp>
|
||||
*
|
||||
* FIXME: this is fooled by code such as
|
||||
*
|
||||
* result = if <exp>
|
||||
* <a>
|
||||
* else
|
||||
* <b>
|
||||
* end
|
||||
*
|
||||
* FIXME: we're also fooled if someone does something heinous such as
|
||||
*
|
||||
* puts("hello") \
|
||||
* unless <exp>
|
||||
*/
|
||||
if (canMatch (&cp, "case") || canMatch (&cp, "for") ||
|
||||
canMatch (&cp, "if") || canMatch (&cp, "unless") ||
|
||||
canMatch (&cp, "while"))
|
||||
{
|
||||
enterUnnamedScope ();
|
||||
}
|
||||
|
||||
/*
|
||||
* "module M", "class C" and "def m" should only be at the beginning
|
||||
* of a line.
|
||||
*/
|
||||
if (canMatch (&cp, "module"))
|
||||
{
|
||||
readAndEmitTag (&cp, K_MODULE);
|
||||
}
|
||||
else if (canMatch (&cp, "class"))
|
||||
{
|
||||
readAndEmitTag (&cp, K_CLASS);
|
||||
}
|
||||
else if (canMatch (&cp, "def"))
|
||||
{
|
||||
readAndEmitTag (&cp, K_METHOD);
|
||||
}
|
||||
|
||||
while (*cp != '\0')
|
||||
{
|
||||
/* FIXME: we don't cope with here documents, or string literals,
|
||||
* or regular expression literals, or ... you get the idea.
|
||||
* Hopefully, the restriction above that insists on seeing
|
||||
* definitions at the starts of lines should keep us out of
|
||||
* mischief.
|
||||
*/
|
||||
if (inMultiLineComment || isspace (*cp))
|
||||
{
|
||||
++cp;
|
||||
}
|
||||
else if (*cp == '#')
|
||||
{
|
||||
/* FIXME: this is wrong, but there *probably* won't be a
|
||||
* definition after an interpolated string (where # doesn't
|
||||
* mean 'comment').
|
||||
*/
|
||||
break;
|
||||
}
|
||||
else if (canMatch (&cp, "begin") || canMatch (&cp, "do"))
|
||||
{
|
||||
enterUnnamedScope ();
|
||||
}
|
||||
else if (canMatch (&cp, "end") && stringListCount (nesting) > 0)
|
||||
{
|
||||
/* Leave the most recent scope. */
|
||||
vStringDelete (stringListLast (nesting));
|
||||
stringListRemoveLast (nesting);
|
||||
}
|
||||
else if (*cp != '\0')
|
||||
{
|
||||
do
|
||||
++cp;
|
||||
while (isalnum (*cp) || *cp == '_');
|
||||
}
|
||||
}
|
||||
}
|
||||
stringListDelete (nesting);
|
||||
}
|
||||
|
||||
extern parserDefinition* RubyParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "rb", "ruby", NULL };
|
||||
parserDefinition* def = parserNew ("Ruby");
|
||||
def->kinds = RubyKinds;
|
||||
def->kindCount = KIND_COUNT (RubyKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findRubyTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* $Id: scheme.c,v 1.3 2006/05/30 04:37:13 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for Scheme language
|
||||
* files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_FUNCTION, K_SET
|
||||
} schemeKind;
|
||||
|
||||
static kindOption SchemeKinds [] = {
|
||||
{ TRUE, 'f', "function", "functions" },
|
||||
{ TRUE, 's', "set", "sets" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Algorithm adapted from from GNU etags.
|
||||
* Scheme tag functions
|
||||
* look for (def... xyzzy
|
||||
* look for (def... (xyzzy
|
||||
* look for (def ... ((... (xyzzy ....
|
||||
* look for (set! xyzzy
|
||||
*/
|
||||
static void readIdentifier (vString *const name, const unsigned char *cp)
|
||||
{
|
||||
const unsigned char *p;
|
||||
vStringClear (name);
|
||||
/* Go till you get to white space or a syntactic break */
|
||||
for (p = cp; *p != '\0' && *p != '(' && *p != ')' && !isspace (*p); p++)
|
||||
vStringPut (name, (int) *p);
|
||||
vStringTerminate (name);
|
||||
}
|
||||
|
||||
static void findSchemeTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = line;
|
||||
|
||||
if (cp [0] == '(' &&
|
||||
(cp [1] == 'D' || cp [1] == 'd') &&
|
||||
(cp [2] == 'E' || cp [2] == 'e') &&
|
||||
(cp [3] == 'F' || cp [3] == 'f'))
|
||||
{
|
||||
while (!isspace (*cp))
|
||||
cp++;
|
||||
/* Skip over open parens and white space */
|
||||
while (*cp != '\0' && (isspace (*cp) || *cp == '('))
|
||||
cp++;
|
||||
readIdentifier (name, cp);
|
||||
makeSimpleTag (name, SchemeKinds, K_FUNCTION);
|
||||
}
|
||||
if (cp [0] == '(' &&
|
||||
(cp [1] == 'S' || cp [1] == 's') &&
|
||||
(cp [2] == 'E' || cp [2] == 'e') &&
|
||||
(cp [3] == 'T' || cp [3] == 't') &&
|
||||
(cp [4] == '!' || cp [4] == '!') &&
|
||||
(isspace (cp [5])))
|
||||
{
|
||||
while (*cp != '\0' && !isspace (*cp))
|
||||
cp++;
|
||||
/* Skip over white space */
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
readIdentifier (name, cp);
|
||||
makeSimpleTag (name, SchemeKinds, K_SET);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* SchemeParser (void)
|
||||
{
|
||||
static const char *const extensions [] = {
|
||||
"SCM", "SM", "sch", "scheme", "scm", "sm", NULL
|
||||
};
|
||||
parserDefinition* def = parserNew ("Scheme");
|
||||
def->kinds = SchemeKinds;
|
||||
def->kindCount = KIND_COUNT (SchemeKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findSchemeTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* $Id: sh.c,v 1.6 2006/05/30 04:37:13 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for scripts for the
|
||||
* Bourne shell (and its derivatives, the Korn and Z shells).
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_FUNCTION
|
||||
} shKind;
|
||||
|
||||
static kindOption ShKinds [] = {
|
||||
{ TRUE, 'f', "function", "functions"}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Reject any tag "main" from a file named "configure". These appear in
|
||||
* here-documents in GNU autoconf scripts and will add a haystack to the
|
||||
* needle.
|
||||
*/
|
||||
static boolean hackReject (const vString* const tagName)
|
||||
{
|
||||
const char *const scriptName = baseFilename (vStringValue (File.name));
|
||||
boolean result = (boolean) (
|
||||
strcmp (scriptName, "configure") == 0 &&
|
||||
strcmp (vStringValue (tagName), "main") == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void findShTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char* cp = line;
|
||||
boolean functionFound = FALSE;
|
||||
|
||||
if (line [0] == '#')
|
||||
continue;
|
||||
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
if (strncmp ((const char*) cp, "function", (size_t) 8) == 0 &&
|
||||
isspace ((int) cp [8]))
|
||||
{
|
||||
functionFound = TRUE;
|
||||
cp += 8;
|
||||
if (! isspace ((int) *cp))
|
||||
continue;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
}
|
||||
if (! (isalnum ((int) *cp) || *cp == '_'))
|
||||
continue;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (*cp++ == '(')
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (*cp == ')' && ! hackReject (name))
|
||||
functionFound = TRUE;
|
||||
}
|
||||
if (functionFound)
|
||||
makeSimpleTag (name, ShKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* ShParser (void)
|
||||
{
|
||||
static const char *const extensions [] = {
|
||||
"sh", "SH", "bsh", "bash", "ksh", "zsh", NULL
|
||||
};
|
||||
parserDefinition* def = parserNew ("Sh");
|
||||
def->kinds = ShKinds;
|
||||
def->kindCount = KIND_COUNT (ShKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findShTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id: slang.c,v 1.5 2006/05/30 04:37:13 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2001, Francesc Rocher
|
||||
*
|
||||
* Author: Francesc Rocher <f.rocher@computer.org>.
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for S-Lang files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
static void installSlangRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language,
|
||||
"^.*define[ \t]+([A-Z_][A-Z0-9_]*)[^;]*$",
|
||||
"\\1", "f,function,functions", "i");
|
||||
addTagRegex (language,
|
||||
"^[ \t]*implements[ \t]+\\([ \t]*\"([^\"]*)\"[ \t]*\\)[ \t]*;",
|
||||
"\\1", "n,namespace,namespaces", NULL);
|
||||
}
|
||||
|
||||
extern parserDefinition* SlangParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "sl", NULL };
|
||||
parserDefinition* const def = parserNew ("SLang");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installSlangRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* $Id: sml.c,v 1.7 2006/05/30 04:37:13 darren Exp $
|
||||
*
|
||||
* Copyright (c) 2002, Venkatesh Prasad Ranganath and Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions for generating tags for SML language files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "entry.h"
|
||||
#include "parse.h"
|
||||
#include "read.h"
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_AND = -2,
|
||||
K_NONE = -1,
|
||||
K_EXCEPTION,
|
||||
K_FUNCTION,
|
||||
K_FUNCTOR,
|
||||
K_SIGNATURE,
|
||||
K_STRUCTURE,
|
||||
K_TYPE,
|
||||
K_VAL
|
||||
} smlKind;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static kindOption SmlKinds[] = {
|
||||
{ TRUE, 'e', "exception", "exception declarations" },
|
||||
{ TRUE, 'f', "function", "function definitions" },
|
||||
{ TRUE, 'c', "functor", "functor definitions" },
|
||||
{ TRUE, 's', "signature", "signature declarations" },
|
||||
{ TRUE, 'r', "structure", "structure declarations" },
|
||||
{ TRUE, 't', "type", "type definitions" },
|
||||
{ TRUE, 'v', "value", "value bindings" }
|
||||
};
|
||||
|
||||
static struct {
|
||||
const char *keyword;
|
||||
smlKind kind;
|
||||
} SmlKeywordTypes [] = {
|
||||
{ "abstype", K_TYPE },
|
||||
{ "and", K_AND },
|
||||
{ "datatype", K_TYPE },
|
||||
{ "exception", K_EXCEPTION },
|
||||
{ "functor", K_FUNCTOR },
|
||||
{ "fun", K_FUNCTION },
|
||||
{ "signature", K_SIGNATURE },
|
||||
{ "structure", K_STRUCTURE },
|
||||
{ "type", K_TYPE },
|
||||
{ "val", K_VAL }
|
||||
};
|
||||
|
||||
static unsigned int CommentLevel = 0;
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void makeSmlTag (smlKind type, vString *name)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (name));
|
||||
tag.kindName = SmlKinds [type].name;
|
||||
tag.kind = SmlKinds [type].letter;
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
|
||||
static const unsigned char *skipSpace (const unsigned char *cp)
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
static boolean isIdentifier (int c)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
/* Consider '_' as an delimiter to aid user in tracking it's usage. */
|
||||
const char *const alternateIdentifiers = "!%&$#+-<>=/?@\\~'^|*_";
|
||||
if (isalnum (c))
|
||||
result = TRUE;
|
||||
else if (c != '\0' && strchr (alternateIdentifiers, c) != NULL)
|
||||
result = TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
static const unsigned char *parseIdentifier (
|
||||
const unsigned char *cp, vString *const identifier)
|
||||
{
|
||||
boolean stringLit = FALSE;
|
||||
vStringClear (identifier);
|
||||
while (*cp != '\0' && (!isIdentifier ((int) *cp) || stringLit))
|
||||
{
|
||||
int oneback = *cp;
|
||||
cp++;
|
||||
if (oneback == '(' && *cp == '*' && stringLit == FALSE)
|
||||
{
|
||||
CommentLevel++;
|
||||
return ++cp;
|
||||
}
|
||||
if (*cp == '"' && oneback != '\\')
|
||||
{
|
||||
stringLit = TRUE;
|
||||
continue;
|
||||
}
|
||||
if (stringLit && *cp == '"' && oneback != '\\')
|
||||
stringLit = FALSE;
|
||||
}
|
||||
if (strcmp ((const char *) cp, "") == 0 || cp == NULL)
|
||||
return cp;
|
||||
|
||||
while (isIdentifier ((int) *cp))
|
||||
{
|
||||
vStringPut (identifier, (int) *cp);
|
||||
cp++;
|
||||
}
|
||||
vStringTerminate (identifier);
|
||||
return cp;
|
||||
}
|
||||
|
||||
static smlKind findNextIdentifier (const unsigned char **cp)
|
||||
{
|
||||
smlKind result = K_NONE;
|
||||
vString *const identifier = vStringNew ();
|
||||
unsigned int count = sizeof (SmlKeywordTypes) / sizeof (SmlKeywordTypes [0]);
|
||||
unsigned int i;
|
||||
*cp = parseIdentifier (*cp, identifier);
|
||||
for (i = 0 ; i < count && result == K_NONE ; ++i)
|
||||
{
|
||||
const char *id = vStringValue (identifier);
|
||||
if (strcmp (id, SmlKeywordTypes [i].keyword) == 0)
|
||||
result = SmlKeywordTypes [i].kind;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void findSmlTags (void)
|
||||
{
|
||||
vString *const identifier = vStringNew ();
|
||||
const unsigned char *line;
|
||||
smlKind lastTag = K_NONE;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp = skipSpace (line);
|
||||
do
|
||||
{
|
||||
smlKind foundTag;
|
||||
if (CommentLevel != 0)
|
||||
{
|
||||
cp = (const unsigned char *) strstr ((const char *) cp, "*)");
|
||||
if (cp == NULL)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
--CommentLevel;
|
||||
cp += 2;
|
||||
}
|
||||
}
|
||||
foundTag = findNextIdentifier (&cp);
|
||||
if (foundTag != K_NONE)
|
||||
{
|
||||
cp = skipSpace (cp);
|
||||
cp = parseIdentifier (cp, identifier);
|
||||
if (foundTag == K_AND)
|
||||
makeSmlTag (lastTag, identifier);
|
||||
else
|
||||
{
|
||||
makeSmlTag (foundTag, identifier);
|
||||
lastTag = foundTag;
|
||||
}
|
||||
}
|
||||
if (strstr ((const char *) cp, "(*") != NULL)
|
||||
{
|
||||
cp += 2;
|
||||
cp = (const unsigned char *) strstr ((const char *) cp, "*)");
|
||||
if (cp == NULL)
|
||||
++CommentLevel;
|
||||
}
|
||||
} while (cp != NULL && strcmp ((const char *) cp, "") != 0);
|
||||
}
|
||||
vStringDelete (identifier);
|
||||
}
|
||||
|
||||
extern parserDefinition *SmlParser (void)
|
||||
{
|
||||
static const char *const extensions[] = { "sml", "sig", NULL };
|
||||
parserDefinition *def = parserNew ("SML");
|
||||
def->kinds = SmlKinds;
|
||||
def->kindCount = KIND_COUNT (SmlKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findSmlTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* $Id: sort.c,v 1.5 2006/05/30 04:37:13 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1996-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions to sort the tag entries.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#if defined (HAVE_STDLIB_H)
|
||||
# include <stdlib.h> /* to declare malloc () */
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "entry.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "sort.h"
|
||||
|
||||
#ifdef TRAP_MEMORY_CALLS
|
||||
# include "safe_malloc.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern void catFile (const char *const name)
|
||||
{
|
||||
FILE *const fp = fopen (name, "r");
|
||||
|
||||
if (fp != NULL)
|
||||
{
|
||||
int c;
|
||||
while ((c = getc (fp)) != EOF)
|
||||
putchar (c);
|
||||
fflush (stdout);
|
||||
fclose (fp);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EXTERNAL_SORT
|
||||
|
||||
#ifdef NON_CONST_PUTENV_PROTOTYPE
|
||||
# define PE_CONST
|
||||
#else
|
||||
# define PE_CONST const
|
||||
#endif
|
||||
|
||||
extern void externalSortTags (const boolean toStdout)
|
||||
{
|
||||
const char *const sortNormalCommand = "sort -u -o";
|
||||
const char *const sortFoldedCommand = "sort -u -f -o";
|
||||
const char *sortCommand =
|
||||
Option.sorted == SO_FOLDSORTED ? sortFoldedCommand : sortNormalCommand;
|
||||
PE_CONST char *const sortOrder1 = "LC_COLLATE=C";
|
||||
PE_CONST char *const sortOrder2 = "LC_ALL=C";
|
||||
const size_t length = 4 + strlen (sortOrder1) + strlen (sortOrder2) +
|
||||
strlen (sortCommand) + (2 * strlen (tagFileName ()));
|
||||
char *const cmd = (char *) malloc (length + 1);
|
||||
int ret = -1;
|
||||
|
||||
if (cmd != NULL)
|
||||
{
|
||||
/* Ensure ASCII value sort order.
|
||||
*/
|
||||
#ifdef HAVE_SETENV
|
||||
setenv ("LC_COLLATE", "C", 1);
|
||||
setenv ("LC_ALL", "C", 1);
|
||||
sprintf (cmd, "%s %s %s", sortCommand, tagFileName (), tagFileName ());
|
||||
#else
|
||||
# ifdef HAVE_PUTENV
|
||||
putenv (sortOrder1);
|
||||
putenv (sortOrder2);
|
||||
sprintf (cmd, "%s %s %s", sortCommand, tagFileName (), tagFileName ());
|
||||
# else
|
||||
sprintf (cmd, "%s %s %s %s %s", sortOrder1, sortOrder2, sortCommand,
|
||||
tagFileName (), tagFileName ());
|
||||
# endif
|
||||
#endif
|
||||
verbose ("system (\"%s\")\n", cmd);
|
||||
ret = system (cmd);
|
||||
free (cmd);
|
||||
|
||||
}
|
||||
if (ret != 0)
|
||||
error (FATAL | PERROR, "cannot sort tag file");
|
||||
else if (toStdout)
|
||||
catFile (tagFileName ());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* These functions provide a basic internal sort. No great memory
|
||||
* optimization is performed (e.g. recursive subdivided sorts),
|
||||
* so have lots of memory if you have large tag files.
|
||||
*/
|
||||
|
||||
static void failedSort (FILE *const fp, const char* msg)
|
||||
{
|
||||
const char* const cannotSort = "cannot sort tag file";
|
||||
if (fp != NULL)
|
||||
fclose (fp);
|
||||
if (msg == NULL)
|
||||
error (FATAL | PERROR, cannotSort);
|
||||
else
|
||||
error (FATAL, "%s: %s", msg, cannotSort);
|
||||
}
|
||||
|
||||
static int compareTagsFolded(const void *const one, const void *const two)
|
||||
{
|
||||
const char *const line1 = *(const char* const*) one;
|
||||
const char *const line2 = *(const char* const*) two;
|
||||
|
||||
return struppercmp (line1, line2);
|
||||
}
|
||||
|
||||
static int compareTags (const void *const one, const void *const two)
|
||||
{
|
||||
const char *const line1 = *(const char* const*) one;
|
||||
const char *const line2 = *(const char* const*) two;
|
||||
|
||||
return strcmp (line1, line2);
|
||||
}
|
||||
|
||||
static void writeSortedTags (
|
||||
char **const table, const size_t numTags, const boolean toStdout)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t i;
|
||||
|
||||
/* Write the sorted lines back into the tag file.
|
||||
*/
|
||||
if (toStdout)
|
||||
fp = stdout;
|
||||
else
|
||||
{
|
||||
fp = fopen (tagFileName (), "w");
|
||||
if (fp == NULL)
|
||||
failedSort (fp, NULL);
|
||||
}
|
||||
for (i = 0 ; i < numTags ; ++i)
|
||||
{
|
||||
/* Here we filter out identical tag *lines* (including search
|
||||
* pattern) if this is not an xref file.
|
||||
*/
|
||||
if (i == 0 || Option.xref || strcmp (table [i], table [i-1]) != 0)
|
||||
if (fputs (table [i], fp) == EOF)
|
||||
failedSort (fp, NULL);
|
||||
}
|
||||
if (toStdout)
|
||||
fflush (fp);
|
||||
else
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
extern void internalSortTags (const boolean toStdout)
|
||||
{
|
||||
vString *vLine = vStringNew ();
|
||||
FILE *fp = NULL;
|
||||
const char *line;
|
||||
size_t i;
|
||||
int (*cmpFunc)(const void *, const void *);
|
||||
|
||||
/* Allocate a table of line pointers to be sorted.
|
||||
*/
|
||||
size_t numTags = TagFile.numTags.added + TagFile.numTags.prev;
|
||||
const size_t tableSize = numTags * sizeof (char *);
|
||||
char **const table = (char **) malloc (tableSize); /* line pointers */
|
||||
DebugStatement ( size_t mallocSize = tableSize; ) /* cumulative total */
|
||||
|
||||
|
||||
cmpFunc = Option.sorted == SO_FOLDSORTED ? compareTagsFolded : compareTags;
|
||||
if (table == NULL)
|
||||
failedSort (fp, "out of memory");
|
||||
|
||||
/* Open the tag file and place its lines into allocated buffers.
|
||||
*/
|
||||
fp = fopen (tagFileName (), "r");
|
||||
if (fp == NULL)
|
||||
failedSort (fp, NULL);
|
||||
for (i = 0 ; i < numTags && ! feof (fp) ; )
|
||||
{
|
||||
line = readLine (vLine, fp);
|
||||
if (line == NULL)
|
||||
{
|
||||
if (! feof (fp))
|
||||
failedSort (fp, NULL);
|
||||
break;
|
||||
}
|
||||
else if (*line == '\0' || strcmp (line, "\n") == 0)
|
||||
; /* ignore blank lines */
|
||||
else
|
||||
{
|
||||
const size_t stringSize = strlen (line) + 1;
|
||||
|
||||
table [i] = (char *) malloc (stringSize);
|
||||
if (table [i] == NULL)
|
||||
failedSort (fp, "out of memory");
|
||||
DebugStatement ( mallocSize += stringSize; )
|
||||
strcpy (table [i], line);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
numTags = i;
|
||||
fclose (fp);
|
||||
vStringDelete (vLine);
|
||||
|
||||
/* Sort the lines.
|
||||
*/
|
||||
qsort (table, numTags, sizeof (*table), cmpFunc);
|
||||
|
||||
writeSortedTags (table, numTags, toStdout);
|
||||
|
||||
PrintStatus (("sort memory: %ld bytes\n", (long) mallocSize));
|
||||
for (i = 0 ; i < numTags ; ++i)
|
||||
free (table [i]);
|
||||
free (table);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $Id: sort.h,v 1.3 2006/05/30 04:37:13 darren Exp $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* External interface to sort.c
|
||||
*/
|
||||
#ifndef _SORT_H
|
||||
#define _SORT_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern void catFile (const char *const name);
|
||||
|
||||
#ifdef EXTERNAL_SORT
|
||||
extern void externalSortTags (const boolean toStdout);
|
||||
#else
|
||||
extern void internalSortTags (const boolean toStdout);
|
||||
#endif
|
||||
|
||||
#endif /* _SORT_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
||||
@@ -0,0 +1,103 @@
|
||||
# $Id: source.mak,v 1.15 2003/04/01 05:02:13 darren Exp $
|
||||
#
|
||||
# Shared macros
|
||||
|
||||
HEADERS = \
|
||||
args.h ctags.h debug.h entry.h general.h get.h keyword.h \
|
||||
main.h options.h parse.h parsers.h read.h routines.h sort.h \
|
||||
strlist.h vstring.h
|
||||
|
||||
SOURCES = \
|
||||
args.c \
|
||||
asm.c \
|
||||
asp.c \
|
||||
awk.c \
|
||||
beta.c \
|
||||
c.c \
|
||||
cobol.c \
|
||||
eiffel.c \
|
||||
entry.c \
|
||||
erlang.c \
|
||||
fortran.c \
|
||||
get.c \
|
||||
html.c \
|
||||
jscript.c \
|
||||
keyword.c \
|
||||
lisp.c \
|
||||
lregex.c \
|
||||
lua.c \
|
||||
main.c \
|
||||
make.c \
|
||||
options.c \
|
||||
parse.c \
|
||||
pascal.c \
|
||||
perl.c \
|
||||
php.c \
|
||||
python.c \
|
||||
read.c \
|
||||
rexx.c \
|
||||
routines.c \
|
||||
ruby.c \
|
||||
scheme.c \
|
||||
sh.c \
|
||||
slang.c \
|
||||
sml.c \
|
||||
sort.c \
|
||||
sql.c \
|
||||
strlist.c \
|
||||
tcl.c \
|
||||
verilog.c \
|
||||
vim.c \
|
||||
yacc.c \
|
||||
vstring.c
|
||||
|
||||
ENVIRONMENT_HEADERS = \
|
||||
e_amiga.h e_djgpp.h e_mac.h e_msoft.h e_os2.h e_qdos.h e_riscos.h e_vms.h
|
||||
|
||||
ENVIRONMENT_SOURCES = \
|
||||
argproc.c mac.c qdos.c
|
||||
|
||||
OBJECTS = \
|
||||
args.$(OBJEXT) \
|
||||
asm.$(OBJEXT) \
|
||||
asp.$(OBJEXT) \
|
||||
awk.$(OBJEXT) \
|
||||
beta.$(OBJEXT) \
|
||||
c.$(OBJEXT) \
|
||||
cobol.$(OBJEXT) \
|
||||
eiffel.$(OBJEXT) \
|
||||
entry.$(OBJEXT) \
|
||||
erlang.$(OBJEXT) \
|
||||
fortran.$(OBJEXT) \
|
||||
get.$(OBJEXT) \
|
||||
html.$(OBJEXT) \
|
||||
jscript.$(OBJEXT) \
|
||||
keyword.$(OBJEXT) \
|
||||
lisp.$(OBJEXT) \
|
||||
lregex.$(OBJEXT) \
|
||||
lua.$(OBJEXT) \
|
||||
main.$(OBJEXT) \
|
||||
make.$(OBJEXT) \
|
||||
options.$(OBJEXT) \
|
||||
parse.$(OBJEXT) \
|
||||
pascal.$(OBJEXT) \
|
||||
perl.$(OBJEXT) \
|
||||
php.$(OBJEXT) \
|
||||
python.$(OBJEXT) \
|
||||
read.$(OBJEXT) \
|
||||
rexx.$(OBJEXT) \
|
||||
routines.$(OBJEXT) \
|
||||
ruby.$(OBJEXT) \
|
||||
scheme.$(OBJEXT) \
|
||||
sh.$(OBJEXT) \
|
||||
slang.$(OBJEXT) \
|
||||
sml.$(OBJEXT) \
|
||||
sort.$(OBJEXT) \
|
||||
sql.$(OBJEXT) \
|
||||
strlist.$(OBJEXT) \
|
||||
tcl.$(OBJEXT) \
|
||||
verilog.$(OBJEXT) \
|
||||
vim.$(OBJEXT) \
|
||||
yacc.$(OBJEXT) \
|
||||
vstring.$(OBJEXT)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user