mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Import exuberant ctags
This commit is contained in:
parent
d55b019284
commit
711bd095db
79 changed files with 34966 additions and 9 deletions
15
Makefile
15
Makefile
|
@ -176,6 +176,7 @@ include third_party/tr/tr.mk
|
|||
include third_party/sed/sed.mk
|
||||
include third_party/awk/awk.mk
|
||||
include third_party/make/make.mk
|
||||
include third_party/ctags/ctags.mk
|
||||
include third_party/finger/finger.mk
|
||||
include third_party/argon2/argon2.mk
|
||||
include third_party/smallz4/smallz4.mk
|
||||
|
@ -278,14 +279,14 @@ o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS
|
|||
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
|
||||
|
||||
TAGS: private .UNSANDBOXED = 1
|
||||
TAGS: o/$(MODE)/srcs-old.txt $(SRCS)
|
||||
TAGS: o/$(MODE)/srcs-old.txt $(SRCS) o/$(MODE)/third_party/ctags/ctags.com
|
||||
@$(RM) $@
|
||||
@$(TAGS) $(TAGSFLAGS) -L $< -o $@
|
||||
@o/$(MODE)/third_party/ctags/ctags.com $(TAGSFLAGS) -L $< -o $@
|
||||
|
||||
HTAGS: private .UNSANDBOXED = 1
|
||||
HTAGS: o/$(MODE)/hdrs-old.txt $(HDRS)
|
||||
HTAGS: o/$(MODE)/hdrs-old.txt $(HDRS) o/$(MODE)/third_party/ctags/ctags.com
|
||||
@$(RM) $@
|
||||
@build/htags -L $< -o $@
|
||||
@build/htags o/$(MODE)/third_party/ctags/ctags.com -L $< -o $@
|
||||
|
||||
loc: private .UNSANDBOXED = 1
|
||||
loc: o/$(MODE)/tool/build/summy.com
|
||||
|
@ -438,9 +439,9 @@ $(SRCS):
|
|||
$(HDRS):
|
||||
$(INCS):
|
||||
.DEFAULT:
|
||||
@$(ECHO) >&2
|
||||
@$(ECHO) NOTE: deleting o/$(MODE)/depend because of an unspecified prerequisite: $@ >&2
|
||||
@$(ECHO) >&2
|
||||
@$(ECHO)
|
||||
@$(ECHO) NOTE: deleting o/$(MODE)/depend because of an unspecified prerequisite: $@
|
||||
@$(ECHO)
|
||||
$(RM) o/$(MODE)/depend
|
||||
|
||||
-include o/$(MODE)/depend
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
LC_ALL = C
|
||||
SOURCE_DATE_EPOCH = 0
|
||||
|
||||
TAGS ?= /usr/bin/ctags # emacs source builds or something breaks it
|
||||
ARFLAGS = rcsD
|
||||
ZFLAGS ?=
|
||||
XARGS ?= xargs -P4 -rs8000
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
# '(progn
|
||||
# (add-hook 'c-mode-common-hook 'jart-c-mode-common-hook)))
|
||||
|
||||
TAGS="$1"
|
||||
shift
|
||||
|
||||
# ctags doesn't understand atomics, e.g.
|
||||
# extern char **environ;
|
||||
set -- --regex-c='/_Atomic(\([^)]*\))/\1/b' "$@"
|
||||
|
@ -63,7 +66,7 @@ set -- --regex-c='/^extern [^(]*(\*const \([^)]*\))(/\1/b' "$@"
|
|||
# struct WorstSoftwareEver;
|
||||
set -- --regex-c='/^struct.*;$/uehocruehcroue/b' "$@"
|
||||
|
||||
exec ${TAGS:-ctags} \
|
||||
exec $TAGS \
|
||||
-e \
|
||||
--langmap=c:.c.h \
|
||||
--exclude=libc/nt/struct/imagefileheader.internal.h \
|
||||
|
|
341
third_party/ctags/COPYING
vendored
Normal file
341
third_party/ctags/COPYING
vendored
Normal file
|
@ -0,0 +1,341 @@
|
|||
// clang-format off
|
||||
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.
|
16
third_party/ctags/README.cosmo
vendored
Normal file
16
third_party/ctags/README.cosmo
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// clang-format off
|
||||
ORIGIN
|
||||
|
||||
Debian 11
|
||||
Package: exuberant-ctags
|
||||
Version: 1:5.9~svn20110310-14
|
||||
|
||||
LICENSE
|
||||
|
||||
GNU GPL v2
|
||||
|
||||
LOCAL CHANGES
|
||||
|
||||
- Rename __unused__ to __unused
|
||||
- Use [[:alnum:]] rather than invalid strings
|
||||
- Remove support for VAX, VMS, OS2, QDOS, Amiga, etc.
|
44
third_party/ctags/ant.c
vendored
Normal file
44
third_party/ctags/ant.c
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2008, David Fishburn
|
||||
*
|
||||
* 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 Ant language files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installAntRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language,
|
||||
"^[ \t]*<[ \t]*project[^>]+name=\"([^\"]+)\".*", "\\1", "p,project,projects", NULL);
|
||||
addTagRegex (language,
|
||||
"^[ \t]*<[ \t]*target[^>]+name=\"([^\"]+)\".*", "\\1", "t,target,targets", NULL);
|
||||
}
|
||||
|
||||
extern parserDefinition* AntParser ()
|
||||
{
|
||||
static const char *const extensions [] = { "build.xml", NULL };
|
||||
parserDefinition* const def = parserNew ("Ant");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installAntRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
268
third_party/ctags/args.c
vendored
Normal file
268
third_party/ctags/args.c
vendored
Normal file
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* $Id: args.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
/**/
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/ctags/args.h"
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
* 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);
|
||||
vStringStripTrailing (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: */
|
71
third_party/ctags/args.h
vendored
Normal file
71
third_party/ctags/args.h
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: args.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.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: */
|
389
third_party/ctags/asm.c
vendored
Normal file
389
third_party/ctags/asm.c
vendored
Normal file
|
@ -0,0 +1,389 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: asm.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/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)
|
||||
{
|
||||
vString *keyword = vStringNew ();
|
||||
opKeyword result;
|
||||
|
||||
vStringCopyToLower (keyword, op);
|
||||
result = (opKeyword) lookupKeyword (vStringValue (keyword), Lang_asm);
|
||||
vStringDelete (keyword);
|
||||
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: */
|
330
third_party/ctags/asp.c
vendored
Normal file
330
third_party/ctags/asp.c
vendored
Normal file
|
@ -0,0 +1,330 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: asp.c 711 2009-07-04 16:52:11Z dhiebert $
|
||||
*
|
||||
* Copyright (c) 2000, Patrick Dehne <patrick@steidle.net>
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CONST, K_CLASS, K_FUNCTION, K_SUB, K_DIM
|
||||
} aspKind;
|
||||
|
||||
static kindOption AspKinds [] = {
|
||||
{ TRUE, 'd', "constant", "constants"},
|
||||
{ TRUE, 'c', "class", "classes"},
|
||||
{ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* class member? */
|
||||
else if (strncasecmp ((const char*) cp, "public", (size_t) 6) == 0)
|
||||
{
|
||||
cp += 6;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
|
||||
{
|
||||
cp+=8;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
|
||||
{
|
||||
cp+=3;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_SUB);
|
||||
vStringClear (name);
|
||||
}
|
||||
else {
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_DIM);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncasecmp ((const char*) cp, "private", (size_t) 7) == 0)
|
||||
{
|
||||
cp += 7;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
|
||||
{
|
||||
cp+=8;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
|
||||
{
|
||||
cp+=3;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_SUB);
|
||||
vStringClear (name);
|
||||
}
|
||||
else {
|
||||
while (isalnum ((int) *cp) || *cp == '_')
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, AspKinds, K_DIM);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* class declaration? */
|
||||
else if (strncasecmp ((const char*) cp, "class", (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_CLASS);
|
||||
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: */
|
||||
|
83
third_party/ctags/awk.c
vendored
Normal file
83
third_party/ctags/awk.c
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: awk.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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: */
|
205
third_party/ctags/basic.c
vendored
Normal file
205
third_party/ctags/basic.c
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id:$
|
||||
*
|
||||
* Copyright (c) 2000-2006, Darren Hiebert, Elias Pschernig
|
||||
*
|
||||
* 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 BlitzBasic
|
||||
* (BlitzMax), PureBasic and FreeBasic language files. For now, this is kept
|
||||
* quite simple - but feel free to ask for more things added any time -
|
||||
* patches are of course most welcome.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CONST,
|
||||
K_FUNCTION,
|
||||
K_LABEL,
|
||||
K_TYPE,
|
||||
K_VARIABLE,
|
||||
K_ENUM
|
||||
} BasicKind;
|
||||
|
||||
typedef struct {
|
||||
char const *token;
|
||||
BasicKind kind;
|
||||
int skip;
|
||||
} KeyWord;
|
||||
|
||||
static kindOption BasicKinds[] = {
|
||||
{TRUE, 'c', "constant", "constants"},
|
||||
{TRUE, 'f', "function", "functions"},
|
||||
{TRUE, 'l', "label", "labels"},
|
||||
{TRUE, 't', "type", "types"},
|
||||
{TRUE, 'v', "variable", "variables"},
|
||||
{TRUE, 'g', "enum", "enumerations"}
|
||||
};
|
||||
|
||||
static KeyWord blitzbasic_keywords[] = {
|
||||
{"const", K_CONST, 0},
|
||||
{"global", K_VARIABLE, 0},
|
||||
{"dim", K_VARIABLE, 0},
|
||||
{"function", K_FUNCTION, 0},
|
||||
{"type", K_TYPE, 0},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static KeyWord purebasic_keywords[] = {
|
||||
{"newlist", K_VARIABLE, 0},
|
||||
{"global", K_VARIABLE, 0},
|
||||
{"dim", K_VARIABLE, 0},
|
||||
{"procedure", K_FUNCTION, 0},
|
||||
{"interface", K_TYPE, 0},
|
||||
{"structure", K_TYPE, 0},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static KeyWord freebasic_keywords[] = {
|
||||
{"const", K_CONST, 0},
|
||||
{"dim as", K_VARIABLE, 1},
|
||||
{"dim", K_VARIABLE, 0},
|
||||
{"common", K_VARIABLE, 0},
|
||||
{"function", K_FUNCTION, 0},
|
||||
{"sub", K_FUNCTION, 0},
|
||||
{"private sub", K_FUNCTION, 0},
|
||||
{"public sub", K_FUNCTION, 0},
|
||||
{"private function", K_FUNCTION, 0},
|
||||
{"public function", K_FUNCTION, 0},
|
||||
{"type", K_TYPE, 0},
|
||||
{"enum", K_ENUM, 0},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/* Match the name of a tag (function, variable, type, ...) starting at pos. */
|
||||
static char const *extract_name (char const *pos, vString * name)
|
||||
{
|
||||
while (isspace (*pos))
|
||||
pos++;
|
||||
vStringClear (name);
|
||||
for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ','; pos++)
|
||||
vStringPut (name, *pos);
|
||||
vStringTerminate (name);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Match a keyword starting at p (case insensitive). */
|
||||
static int match_keyword (const char *p, KeyWord const *kw)
|
||||
{
|
||||
vString *name;
|
||||
size_t i;
|
||||
int j;
|
||||
for (i = 0; i < strlen (kw->token); i++)
|
||||
{
|
||||
if (tolower (p[i]) != kw->token[i])
|
||||
return 0;
|
||||
}
|
||||
name = vStringNew ();
|
||||
p += i;
|
||||
for (j = 0; j < 1 + kw->skip; j++)
|
||||
{
|
||||
p = extract_name (p, name);
|
||||
}
|
||||
makeSimpleTag (name, BasicKinds, kw->kind);
|
||||
vStringDelete (name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Match a "label:" style label. */
|
||||
static void match_colon_label (char const *p)
|
||||
{
|
||||
char const *end = p + strlen (p) - 1;
|
||||
while (isspace (*end))
|
||||
end--;
|
||||
if (*end == ':')
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
vStringNCatS (name, p, end - p);
|
||||
makeSimpleTag (name, BasicKinds, K_LABEL);
|
||||
vStringDelete (name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Match a ".label" style label. */
|
||||
static void match_dot_label (char const *p)
|
||||
{
|
||||
if (*p == '.')
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
extract_name (p + 1, name);
|
||||
makeSimpleTag (name, BasicKinds, K_LABEL);
|
||||
vStringDelete (name);
|
||||
}
|
||||
}
|
||||
|
||||
static void findBasicTags (void)
|
||||
{
|
||||
const char *line;
|
||||
const char *extension = fileExtension (vStringValue (File.name));
|
||||
KeyWord *keywords;
|
||||
|
||||
if (strcmp (extension, "bb") == 0)
|
||||
keywords = blitzbasic_keywords;
|
||||
else if (strcmp (extension, "pb") == 0)
|
||||
keywords = purebasic_keywords;
|
||||
else
|
||||
keywords = freebasic_keywords;
|
||||
|
||||
while ((line = (const char *) fileReadLine ()) != NULL)
|
||||
{
|
||||
const char *p = line;
|
||||
KeyWord const *kw;
|
||||
|
||||
while (isspace (*p))
|
||||
p++;
|
||||
|
||||
/* Empty line? */
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
/* In Basic, keywords always are at the start of the line. */
|
||||
for (kw = keywords; kw->token; kw++)
|
||||
if (match_keyword (p, kw)) break;
|
||||
|
||||
/* Is it a label? */
|
||||
if (strcmp (extension, "bb") == 0)
|
||||
match_dot_label (p);
|
||||
else
|
||||
match_colon_label (p);
|
||||
}
|
||||
}
|
||||
|
||||
parserDefinition *BasicParser (void)
|
||||
{
|
||||
static char const *extensions[] = { "bas", "bi", "bb", "pb", NULL };
|
||||
parserDefinition *def = parserNew ("Basic");
|
||||
def->kinds = BasicKinds;
|
||||
def->kindCount = KIND_COUNT (BasicKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findBasicTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
323
third_party/ctags/beta.c
vendored
Normal file
323
third_party/ctags/beta.c
vendored
Normal file
|
@ -0,0 +1,323 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: beta.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* Copyright (c) 1999-2000, Mjolner Informatics
|
||||
*
|
||||
* Written by Erik Corry <corry@mjolner.dk>
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/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));
|
||||
vStringDelete (line);
|
||||
}
|
||||
|
||||
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: */
|
2934
third_party/ctags/c.c
vendored
Normal file
2934
third_party/ctags/c.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
51
third_party/ctags/cobol.c
vendored
Normal file
51
third_party/ctags/cobol.c
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: cobol.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
#include "third_party/ctags/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: */
|
279
third_party/ctags/config.h
vendored
Normal file
279
third_party/ctags/config.h
vendored
Normal file
|
@ -0,0 +1,279 @@
|
|||
// clang-format off
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define this label if your system uses case-insensitive file names */
|
||||
/* #undef CASE_INSENSITIVE_FILENAMES */
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* 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 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 to use the system sort utility (which is probably more
|
||||
* efficient) over the internal sorting algorithm.
|
||||
*/
|
||||
#ifndef INTERNAL_SORT
|
||||
# define EXTERNAL_SORT 1
|
||||
#endif
|
||||
|
||||
|
||||
/* 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 `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 HAVE_REGEX 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 this macro if the field "st_ino" exists in struct stat in
|
||||
<sys/stat.h>. */
|
||||
#define HAVE_STAT_ST_INO 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_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 `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 `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. */
|
||||
#define HAVE_SYS_DIR_H 1
|
||||
|
||||
/* 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 as the maximum integer on your system if not defined <limits.h>. */
|
||||
/* #undef INT_MAX */
|
||||
|
||||
/* Define to the appropriate size for tmpnam() if <stdio.h> does not define
|
||||
this. */
|
||||
#define L_tmpnam 20
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_FGETPOS */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_FTRUNCATE */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_GETENV */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_LSTAT */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_MALLOC */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_REMOVE */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_STAT */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_TRUNCATE */
|
||||
|
||||
/* If you receive error or warning messages indicating that you are missing a
|
||||
prototype for, or a type mismatch using, the following function, define
|
||||
this label and remake. */
|
||||
/* #undef NEED_PROTO_UNLINK */
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* Package name. */
|
||||
/* #undef PACKAGE */
|
||||
|
||||
/* 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 home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION ""
|
||||
|
||||
/* Define this label if regcomp() is broken. */
|
||||
/* #undef REGCOMP_BROKEN */
|
||||
|
||||
/* 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 to 1 if all of the C90 standard headers exist (not just the ones
|
||||
required in a freestanding environment). This macro is provided for
|
||||
backward compatibility; new code need not use it. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* 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
|
||||
|
||||
/* If you wish to change the directory in which temporary files are stored,
|
||||
define this label to the directory desired. */
|
||||
#define TMPDIR "/tmp"
|
||||
|
||||
/* Package version. */
|
||||
/* #undef VERSION */
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
/* #undef _FILE_OFFSET_BITS */
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
/* #undef _LARGE_FILES */
|
||||
|
||||
/* 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 to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to long if <stdio.h> does not define this. */
|
||||
/* #undef fpos_t */
|
||||
|
||||
/* Define to `long int' if <sys/types.h> does not define. */
|
||||
/* #undef off_t */
|
||||
|
||||
/* Define remove to unlink if you have unlink(), but not remove(). */
|
||||
/* #undef remove */
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
29
third_party/ctags/ctags.h
vendored
Normal file
29
third_party/ctags/ctags.h
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: ctags.h 702 2009-03-14 03:52:21Z dhiebert $
|
||||
*
|
||||
* 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.9~svn20110310"
|
||||
#endif
|
||||
#define PROGRAM_NAME "Exuberant Ctags"
|
||||
#define PROGRAM_URL "http://ctags.sourceforge.net"
|
||||
#define PROGRAM_COPYRIGHT "Copyright (C) 1996-2009"
|
||||
#define AUTHOR_NAME "Darren Hiebert"
|
||||
#define AUTHOR_EMAIL "dhiebert@users.sourceforge.net"
|
||||
|
||||
#endif /* _CTAGS_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
61
third_party/ctags/ctags.mk
vendored
Normal file
61
third_party/ctags/ctags.mk
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
|
||||
PKGS += THIRD_PARTY_CTAGS
|
||||
|
||||
THIRD_PARTY_CTAGS_ARTIFACTS += THIRD_PARTY_CTAGS_A
|
||||
THIRD_PARTY_CTAGS = $(THIRD_PARTY_CTAGS_DEPS) $(THIRD_PARTY_CTAGS_A)
|
||||
THIRD_PARTY_CTAGS_A = o/$(MODE)/third_party/ctags/ctags.a
|
||||
THIRD_PARTY_CTAGS_FILES := $(wildcard third_party/ctags/*)
|
||||
THIRD_PARTY_CTAGS_HDRS = $(filter %.h,$(THIRD_PARTY_CTAGS_FILES))
|
||||
THIRD_PARTY_CTAGS_INCS = $(filter %.inc,$(THIRD_PARTY_CTAGS_FILES))
|
||||
THIRD_PARTY_CTAGS_SRCS = $(filter %.c,$(THIRD_PARTY_CTAGS_FILES))
|
||||
THIRD_PARTY_CTAGS_OBJS = $(THIRD_PARTY_CTAGS_SRCS:%.c=o/$(MODE)/%.o)
|
||||
|
||||
THIRD_PARTY_CTAGS_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_REGEX
|
||||
|
||||
THIRD_PARTY_CTAGS_DEPS := \
|
||||
$(call uniq,$(foreach x,$(THIRD_PARTY_CTAGS_DIRECTDEPS),$($(x))))
|
||||
|
||||
THIRD_PARTY_CTAGS_CHECKS = \
|
||||
$(THIRD_PARTY_CTAGS_A).pkg \
|
||||
$(THIRD_PARTY_CTAGS_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
$(THIRD_PARTY_CTAGS_A): \
|
||||
third_party/ctags/ \
|
||||
$(THIRD_PARTY_CTAGS_A).pkg \
|
||||
$(THIRD_PARTY_CTAGS_OBJS)
|
||||
|
||||
$(THIRD_PARTY_CTAGS_A).pkg: \
|
||||
$(THIRD_PARTY_CTAGS_OBJS) \
|
||||
$(foreach x,$(THIRD_PARTY_CTAGS_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/third_party/ctags/ctags.com.dbg: \
|
||||
$(THIRD_PARTY_CTAGS) \
|
||||
o/$(MODE)/third_party/ctags/main.o \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
THIRD_PARTY_CTAGS_LIBS = $(THIRD_PARTY_CTAGS_A)
|
||||
THIRD_PARTY_CTAGS_BINS = $(THIRD_PARTY_CTAGS_COMS) $(THIRD_PARTY_CTAGS_COMS:%=%.dbg)
|
||||
THIRD_PARTY_CTAGS_COMS = o/$(MODE)/third_party/ctags/ctags.com
|
||||
$(THIRD_PARTY_CTAGS_OBJS): $(BUILD_FILES) third_party/ctags/ctags.mk
|
||||
|
||||
.PHONY: o/$(MODE)/third_party/ctags
|
||||
o/$(MODE)/third_party/ctags: \
|
||||
$(THIRD_PARTY_CTAGS_BINS) \
|
||||
$(THIRD_PARTY_CTAGS_CHECKS)
|
114
third_party/ctags/debug.c
vendored
Normal file
114
third_party/ctags/debug.c
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: debug.c 558 2007-06-15 19:17:02Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/str/str.h"
|
||||
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/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 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: */
|
71
third_party/ctags/debug.h
vendored
Normal file
71
third_party/ctags/debug.h
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: debug.h 558 2007-06-15 19:17:02Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "libc/assert.h"
|
||||
#endif
|
||||
#include "third_party/ctags/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 debugEntry (const tagEntryInfo *const tag);
|
||||
|
||||
#endif /* _DEBUG_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
44
third_party/ctags/dosbatch.c
vendored
Normal file
44
third_party/ctags/dosbatch.c
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2009, David Fishburn
|
||||
*
|
||||
* 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 DOS Batch language files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installDosBatchRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language,
|
||||
"^:([A-Za-z_0-9]+)", "\\1", "l,label,labels", NULL);
|
||||
addTagRegex (language,
|
||||
"set[ \t]+([A-Za-z_0-9]+)[ \t]*=", "\\1", "v,variable,variables", NULL);
|
||||
}
|
||||
|
||||
extern parserDefinition* DosBatchParser ()
|
||||
{
|
||||
static const char *const extensions [] = { "bat", "cmd", NULL };
|
||||
parserDefinition* const def = parserNew ("DosBatch");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installDosBatchRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
1373
third_party/ctags/eiffel.c
vendored
Normal file
1373
third_party/ctags/eiffel.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
879
third_party/ctags/entry.c
vendored
Normal file
879
third_party/ctags/entry.c
vendored
Normal file
|
@ -0,0 +1,879 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: entry.c 766 2010-09-11 18:59:45Z dhiebert $
|
||||
*
|
||||
* 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 "libc/runtime/runtime.h"
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/str.h" /* to define isspace () */
|
||||
#include "libc/errno.h"
|
||||
|
||||
#if defined (HAVE_SYS_TYPES_H)
|
||||
#include "libc/calls/makedev.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/calls/typedef/u.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/intrin/newbie.h"
|
||||
#include "libc/sock/select.h"
|
||||
#include "libc/sysv/consts/endian.h" /* to declare off_t on some hosts */
|
||||
#endif
|
||||
#if defined (HAVE_TYPES_H)
|
||||
// MISSING #include <types.h> /* to declare off_t on some hosts */
|
||||
#endif
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/runtime/pathconf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/musl/crypt.h"
|
||||
#include "third_party/musl/lockf.h" /* to declare close (), ftruncate (), truncate () */
|
||||
#endif
|
||||
|
||||
/* These header files provide for the functions necessary to do file
|
||||
* truncation.
|
||||
*/
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/posix.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
// MISSING #include <io.h>
|
||||
#endif
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/ctags.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/main.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/sort.h"
|
||||
#include "third_party/ctags/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 (line == NULL)
|
||||
error (FATAL, "bad tag in %s", vStringValue (File.name));
|
||||
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: */
|
111
third_party/ctags/entry.h
vendored
Normal file
111
third_party/ctags/entry.h
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: entry.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.h"
|
||||
|
||||
#include "third_party/ctags/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: */
|
191
third_party/ctags/erlang.c
vendored
Normal file
191
third_party/ctags/erlang.c
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: erlang.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* Copyright (c) 2003, Brent Fulgham <bfulgham@debian.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 Erlang language
|
||||
* files. Some of the parsing constructs are based on the Emacs 'etags'
|
||||
* program by Francesco Potori <pot@gnu.org>
|
||||
*/
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/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: */
|
2431
third_party/ctags/flex.c
vendored
Normal file
2431
third_party/ctags/flex.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
2204
third_party/ctags/fortran.c
vendored
Normal file
2204
third_party/ctags/fortran.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
59
third_party/ctags/general.h
vendored
Normal file
59
third_party/ctags/general.h
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: general.h 508 2007-05-03 03:20:59Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/config.h"
|
||||
|
||||
/* 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 (__GNUG__)
|
||||
# define __unused __attribute__((__unused__))
|
||||
# define __printf(s,f) __attribute__((__format__ (__printf__, s, f)))
|
||||
#else
|
||||
# define __unused
|
||||
# define __printf(s,f)
|
||||
#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
|
||||
|
||||
#endif /* _GENERAL_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
671
third_party/ctags/get.c
vendored
Normal file
671
third_party/ctags/get.c
vendored
Normal file
|
@ -0,0 +1,671 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: get.c 559 2007-06-17 03:30:09Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/get.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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 */
|
||||
boolean hasAtLiteralStrings; /* supports @"c:\" strings */
|
||||
struct sDirective {
|
||||
enum eState state; /* current directive being processed */
|
||||
boolean accept; /* is a directive syntactically 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 */
|
||||
FALSE, /* hasAtLiteralStrings */
|
||||
{
|
||||
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, const boolean hasAtLiteralStrings)
|
||||
{
|
||||
BraceFormat = state;
|
||||
|
||||
Cpp.ungetch = '\0';
|
||||
Cpp.ungetch2 = '\0';
|
||||
Cpp.resolveRequired = FALSE;
|
||||
Cpp.hasAtLiteralStrings = hasAtLiteralStrings;
|
||||
|
||||
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 substitution.
|
||||
*/
|
||||
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 (boolean ignoreBackslash)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = fileGetc ()) != EOF)
|
||||
{
|
||||
if (c == BACKSLASH && ! ignoreBackslash)
|
||||
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 (FALSE);
|
||||
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:
|
||||
if (c == '@' && Cpp.hasAtLiteralStrings)
|
||||
{
|
||||
int next = fileGetc ();
|
||||
if (next == DOUBLE_QUOTE)
|
||||
{
|
||||
Cpp.directive.accept = FALSE;
|
||||
c = skipToEndOfString (TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
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: */
|
51
third_party/ctags/get.h
vendored
Normal file
51
third_party/ctags/get.h
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: get.h 525 2007-05-28 01:50:41Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "third_party/ctags/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, const boolean hasAtLiteralStrings);
|
||||
extern void cppTerminate (void);
|
||||
extern void cppBeginStatement (void);
|
||||
extern void cppEndStatement (void);
|
||||
extern void cppUngetc (const int c);
|
||||
extern int cppGetc (void);
|
||||
extern int skipOverCComment (void);
|
||||
|
||||
#endif /* _GET_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
671
third_party/ctags/go.c
vendored
Normal file
671
third_party/ctags/go.c
vendored
Normal file
|
@ -0,0 +1,671 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/main.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define isType(token,t) (boolean) ((token)->type == (t))
|
||||
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
||||
|
||||
typedef enum eKeywordId {
|
||||
KEYWORD_NONE = -1,
|
||||
KEYWORD_package,
|
||||
KEYWORD_import,
|
||||
KEYWORD_const,
|
||||
KEYWORD_type,
|
||||
KEYWORD_var,
|
||||
KEYWORD_func,
|
||||
KEYWORD_struct,
|
||||
KEYWORD_interface,
|
||||
KEYWORD_map,
|
||||
KEYWORD_chan
|
||||
} keywordId;
|
||||
|
||||
/* Used to determine whether keyword is valid for the current language and
|
||||
* what its ID is.
|
||||
*/
|
||||
typedef struct sKeywordDesc {
|
||||
const char *name;
|
||||
keywordId id;
|
||||
} keywordDesc;
|
||||
|
||||
typedef enum eTokenType {
|
||||
TOKEN_NONE = -1,
|
||||
TOKEN_CHARACTER,
|
||||
// Don't need TOKEN_FORWARD_SLASH
|
||||
TOKEN_FORWARD_SLASH,
|
||||
TOKEN_KEYWORD,
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_STRING,
|
||||
TOKEN_OPEN_PAREN,
|
||||
TOKEN_CLOSE_PAREN,
|
||||
TOKEN_OPEN_CURLY,
|
||||
TOKEN_CLOSE_CURLY,
|
||||
TOKEN_OPEN_SQUARE,
|
||||
TOKEN_CLOSE_SQUARE,
|
||||
TOKEN_SEMICOLON,
|
||||
TOKEN_STAR,
|
||||
TOKEN_LEFT_ARROW,
|
||||
TOKEN_DOT,
|
||||
TOKEN_COMMA
|
||||
} tokenType;
|
||||
|
||||
typedef struct sTokenInfo {
|
||||
tokenType type;
|
||||
keywordId keyword;
|
||||
vString *string; /* the name of the token */
|
||||
unsigned long lineNumber; /* line number of tag */
|
||||
fpos_t filePosition; /* file position of line containing name */
|
||||
} tokenInfo;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
|
||||
static int Lang_go;
|
||||
static jmp_buf Exception;
|
||||
static vString *scope;
|
||||
|
||||
typedef enum {
|
||||
GOTAG_UNDEFINED = -1,
|
||||
GOTAG_PACKAGE,
|
||||
GOTAG_FUNCTION,
|
||||
GOTAG_CONST,
|
||||
GOTAG_TYPE,
|
||||
GOTAG_VAR,
|
||||
} goKind;
|
||||
|
||||
static kindOption GoKinds[] = {
|
||||
{TRUE, 'p', "package", "packages"},
|
||||
{TRUE, 'f', "func", "functions"},
|
||||
{TRUE, 'c', "const", "constants"},
|
||||
{TRUE, 't', "type", "types"},
|
||||
{TRUE, 'v', "var", "variables"}
|
||||
};
|
||||
|
||||
static keywordDesc GoKeywordTable[] = {
|
||||
{"package", KEYWORD_package},
|
||||
{"import", KEYWORD_import},
|
||||
{"const", KEYWORD_const},
|
||||
{"type", KEYWORD_type},
|
||||
{"var", KEYWORD_var},
|
||||
{"func", KEYWORD_func},
|
||||
{"struct", KEYWORD_struct},
|
||||
{"interface", KEYWORD_interface},
|
||||
{"map", KEYWORD_map},
|
||||
{"chan", KEYWORD_chan}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
// XXX UTF-8
|
||||
static boolean isIdentChar (const int c)
|
||||
{
|
||||
return (boolean)
|
||||
(isalpha (c) || isdigit (c) || c == '$' ||
|
||||
c == '@' || c == '_' || c == '#' || c > 128);
|
||||
}
|
||||
|
||||
static void initialize (const langType language)
|
||||
{
|
||||
size_t i;
|
||||
const size_t count =
|
||||
sizeof (GoKeywordTable) / sizeof (GoKeywordTable[0]);
|
||||
Lang_go = language;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
const keywordDesc *const p = &GoKeywordTable[i];
|
||||
addKeyword (p->name, language, (int) p->id);
|
||||
}
|
||||
}
|
||||
|
||||
static tokenInfo *newToken (void)
|
||||
{
|
||||
tokenInfo *const token = xMalloc (1, tokenInfo);
|
||||
token->type = TOKEN_NONE;
|
||||
token->keyword = KEYWORD_NONE;
|
||||
token->string = vStringNew ();
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
return token;
|
||||
}
|
||||
|
||||
static void deleteToken (tokenInfo * const token)
|
||||
{
|
||||
if (token != NULL)
|
||||
{
|
||||
vStringDelete (token->string);
|
||||
eFree (token);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parsing functions
|
||||
*/
|
||||
|
||||
static void parseString (vString *const string, const int delimiter)
|
||||
{
|
||||
boolean end = FALSE;
|
||||
while (!end)
|
||||
{
|
||||
int c = fileGetc ();
|
||||
if (c == EOF)
|
||||
end = TRUE;
|
||||
else if (c == '\\' && delimiter != '`')
|
||||
{
|
||||
c = fileGetc (); /* This maybe a ' or ". */
|
||||
vStringPut (string, c);
|
||||
}
|
||||
else if (c == delimiter)
|
||||
end = TRUE;
|
||||
else
|
||||
vStringPut (string, c);
|
||||
}
|
||||
vStringTerminate (string);
|
||||
}
|
||||
|
||||
static void parseIdentifier (vString *const string, const int firstChar)
|
||||
{
|
||||
int c = firstChar;
|
||||
//Assert (isIdentChar (c));
|
||||
do
|
||||
{
|
||||
vStringPut (string, c);
|
||||
c = fileGetc ();
|
||||
} while (isIdentChar (c));
|
||||
vStringTerminate (string);
|
||||
fileUngetc (c); /* always unget, LF might add a semicolon */
|
||||
}
|
||||
|
||||
static void readToken (tokenInfo *const token)
|
||||
{
|
||||
int c;
|
||||
static tokenType lastTokenType = TOKEN_NONE;
|
||||
|
||||
token->type = TOKEN_NONE;
|
||||
token->keyword = KEYWORD_NONE;
|
||||
vStringClear (token->string);
|
||||
|
||||
getNextChar:
|
||||
do
|
||||
{
|
||||
c = fileGetc ();
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
if (c == '\n' && (lastTokenType == TOKEN_IDENTIFIER ||
|
||||
lastTokenType == TOKEN_STRING ||
|
||||
lastTokenType == TOKEN_CLOSE_PAREN ||
|
||||
lastTokenType == TOKEN_CLOSE_CURLY ||
|
||||
lastTokenType == TOKEN_CLOSE_SQUARE))
|
||||
{
|
||||
token->type = TOKEN_SEMICOLON;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
while (c == '\t' || c == ' ' || c == '\r' || c == '\n');
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case EOF:
|
||||
longjmp (Exception, (int)ExceptionEOF);
|
||||
break;
|
||||
|
||||
case '/':
|
||||
{
|
||||
boolean hasNewline = FALSE;
|
||||
int d = fileGetc ();
|
||||
switch (d)
|
||||
{
|
||||
case '/':
|
||||
fileSkipToCharacter ('\n');
|
||||
/* Line comments start with the
|
||||
* character sequence // and
|
||||
* continue through the next
|
||||
* newline. A line comment acts
|
||||
* like a newline. */
|
||||
fileUngetc ('\n');
|
||||
goto getNextChar;
|
||||
case '*':
|
||||
do
|
||||
{
|
||||
int d;
|
||||
do
|
||||
{
|
||||
d = fileGetc ();
|
||||
if (d == '\n')
|
||||
{
|
||||
hasNewline = TRUE;
|
||||
}
|
||||
} while (d != EOF && d != '*');
|
||||
|
||||
c = fileGetc ();
|
||||
if (c == '/')
|
||||
break;
|
||||
else
|
||||
fileUngetc (c);
|
||||
} while (c != EOF && c != '\0');
|
||||
|
||||
fileUngetc (hasNewline ? '\n' : ' ');
|
||||
goto getNextChar;
|
||||
default:
|
||||
token->type = TOKEN_FORWARD_SLASH;
|
||||
fileUngetc (d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
case '`':
|
||||
token->type = TOKEN_STRING;
|
||||
parseString (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
break;
|
||||
|
||||
case '<':
|
||||
{
|
||||
int d = fileGetc ();
|
||||
if (d == '-')
|
||||
{
|
||||
token->type = TOKEN_LEFT_ARROW;
|
||||
break;
|
||||
}
|
||||
else
|
||||
goto getNextChar;
|
||||
}
|
||||
|
||||
case '(':
|
||||
token->type = TOKEN_OPEN_PAREN;
|
||||
break;
|
||||
|
||||
case ')':
|
||||
token->type = TOKEN_CLOSE_PAREN;
|
||||
break;
|
||||
|
||||
case '{':
|
||||
token->type = TOKEN_OPEN_CURLY;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
token->type = TOKEN_CLOSE_CURLY;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
token->type = TOKEN_OPEN_SQUARE;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
token->type = TOKEN_CLOSE_SQUARE;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
token->type = TOKEN_STAR;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
token->type = TOKEN_DOT;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
token->type = TOKEN_COMMA;
|
||||
break;
|
||||
|
||||
default:
|
||||
parseIdentifier (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
token->keyword = lookupKeyword (vStringValue (token->string), Lang_go);
|
||||
if (isKeyword (token, KEYWORD_NONE))
|
||||
token->type = TOKEN_IDENTIFIER;
|
||||
else
|
||||
token->type = TOKEN_KEYWORD;
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
lastTokenType = token->type;
|
||||
}
|
||||
|
||||
static void skipToMatched (tokenInfo *const token)
|
||||
{
|
||||
int nest_level = 0;
|
||||
tokenType open_token;
|
||||
tokenType close_token;
|
||||
|
||||
switch (token->type)
|
||||
{
|
||||
case TOKEN_OPEN_PAREN:
|
||||
open_token = TOKEN_OPEN_PAREN;
|
||||
close_token = TOKEN_CLOSE_PAREN;
|
||||
break;
|
||||
case TOKEN_OPEN_CURLY:
|
||||
open_token = TOKEN_OPEN_CURLY;
|
||||
close_token = TOKEN_CLOSE_CURLY;
|
||||
break;
|
||||
case TOKEN_OPEN_SQUARE:
|
||||
open_token = TOKEN_OPEN_SQUARE;
|
||||
close_token = TOKEN_CLOSE_SQUARE;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine will skip to a matching closing token.
|
||||
* It will also handle nested tokens like the (, ) below.
|
||||
* ( name varchar(30), text binary(10) )
|
||||
*/
|
||||
if (isType (token, open_token))
|
||||
{
|
||||
nest_level++;
|
||||
while (!(isType (token, close_token) && (nest_level == 0)))
|
||||
{
|
||||
readToken (token);
|
||||
if (isType (token, open_token))
|
||||
{
|
||||
nest_level++;
|
||||
}
|
||||
if (isType (token, close_token))
|
||||
{
|
||||
if (nest_level > 0)
|
||||
{
|
||||
nest_level--;
|
||||
}
|
||||
}
|
||||
}
|
||||
readToken (token);
|
||||
}
|
||||
}
|
||||
|
||||
static void skipType (tokenInfo *const token)
|
||||
{
|
||||
again:
|
||||
// Type = TypeName | TypeLit | "(" Type ")" .
|
||||
if (isType (token, TOKEN_OPEN_PAREN))
|
||||
{
|
||||
skipToMatched (token);
|
||||
return;
|
||||
}
|
||||
|
||||
// TypeName = QualifiedIdent.
|
||||
// QualifiedIdent = [ PackageName "." ] identifier .
|
||||
// PackageName = identifier .
|
||||
if (isType (token, TOKEN_IDENTIFIER))
|
||||
{
|
||||
readToken (token);
|
||||
if (isType (token, TOKEN_DOT))
|
||||
{
|
||||
readToken (token);
|
||||
Assert (isType (token, TOKEN_IDENTIFIER));
|
||||
readToken (token);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// StructType = "struct" "{" { FieldDecl ";" } "}"
|
||||
// InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
|
||||
if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface))
|
||||
{
|
||||
readToken (token);
|
||||
Assert (isType (token, TOKEN_OPEN_CURLY));
|
||||
skipToMatched (token);
|
||||
return;
|
||||
}
|
||||
|
||||
// ArrayType = "[" ArrayLength "]" ElementType .
|
||||
// SliceType = "[" "]" ElementType .
|
||||
// ElementType = Type .
|
||||
if (isType (token, TOKEN_OPEN_SQUARE))
|
||||
{
|
||||
skipToMatched (token);
|
||||
goto again;
|
||||
}
|
||||
|
||||
// PointerType = "*" BaseType .
|
||||
// BaseType = Type .
|
||||
// ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
|
||||
if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
|
||||
{
|
||||
readToken (token);
|
||||
goto again;
|
||||
}
|
||||
|
||||
// MapType = "map" "[" KeyType "]" ElementType .
|
||||
// KeyType = Type .
|
||||
if (isKeyword (token, KEYWORD_map))
|
||||
{
|
||||
readToken (token);
|
||||
Assert (isType (token, TOKEN_OPEN_SQUARE));
|
||||
skipToMatched (token);
|
||||
goto again;
|
||||
}
|
||||
|
||||
// FunctionType = "func" Signature .
|
||||
// Signature = Parameters [ Result ] .
|
||||
// Result = Parameters | Type .
|
||||
// Parameters = "(" [ ParameterList [ "," ] ] ")" .
|
||||
if (isKeyword (token, KEYWORD_func))
|
||||
{
|
||||
readToken (token);
|
||||
Assert (isType (token, TOKEN_OPEN_PAREN));
|
||||
// Parameters
|
||||
skipToMatched (token);
|
||||
// Result is parameters or type or nothing. skipType treats anything
|
||||
// surrounded by parentheses as a type, and does nothing if what
|
||||
// follows is not a type.
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip to the next semicolon, skipping over matching brackets.
|
||||
static void skipToTopLevelSemicolon (tokenInfo *const token)
|
||||
{
|
||||
while (!isType (token, TOKEN_SEMICOLON))
|
||||
{
|
||||
readToken (token);
|
||||
skipToMatched (token);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeTag (tokenInfo *const token, const goKind kind)
|
||||
{
|
||||
const char *const name = vStringValue (token->string);
|
||||
|
||||
tagEntryInfo e;
|
||||
initTagEntry (&e, name);
|
||||
|
||||
if (!GoKinds [kind].enabled)
|
||||
return;
|
||||
|
||||
e.lineNumber = token->lineNumber;
|
||||
e.filePosition = token->filePosition;
|
||||
e.kindName = GoKinds [kind].name;
|
||||
e.kind = GoKinds [kind].letter;
|
||||
|
||||
makeTagEntry (&e);
|
||||
|
||||
if (scope && Option.include.qualifiedTags)
|
||||
{
|
||||
vString *qualifiedName = vStringNew ();
|
||||
vStringCopy (qualifiedName, scope);
|
||||
vStringCatS (qualifiedName, ".");
|
||||
vStringCat (qualifiedName, token->string);
|
||||
e.name = vStringValue (qualifiedName);
|
||||
makeTagEntry (&e);
|
||||
vStringDelete (qualifiedName);
|
||||
}
|
||||
}
|
||||
|
||||
static void parsePackage (tokenInfo *const token)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
|
||||
readToken (name);
|
||||
Assert (isType (name, TOKEN_IDENTIFIER));
|
||||
makeTag (name, GOTAG_PACKAGE);
|
||||
if (!scope && Option.include.qualifiedTags)
|
||||
{
|
||||
scope = vStringNew ();
|
||||
vStringCopy (scope, name->string);
|
||||
}
|
||||
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseFunctionOrMethod (tokenInfo *const token)
|
||||
{
|
||||
// FunctionDecl = "func" identifier Signature [ Body ] .
|
||||
// Body = Block.
|
||||
//
|
||||
// MethodDecl = "func" Receiver MethodName Signature [ Body ] .
|
||||
// Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
|
||||
// BaseTypeName = identifier .
|
||||
tokenInfo *const name = newToken ();
|
||||
|
||||
// Skip over receiver.
|
||||
readToken (name);
|
||||
if (isType (name, TOKEN_OPEN_PAREN))
|
||||
skipToMatched (name);
|
||||
|
||||
Assert (isType (name, TOKEN_IDENTIFIER));
|
||||
|
||||
// Skip over parameters.
|
||||
readToken (token);
|
||||
skipToMatched (token);
|
||||
|
||||
// Skip over result.
|
||||
skipType (token);
|
||||
|
||||
// Skip over function body.
|
||||
if (isType (token, TOKEN_OPEN_CURLY))
|
||||
skipToMatched (token);
|
||||
|
||||
makeTag (name, GOTAG_FUNCTION);
|
||||
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseConstTypeVar (tokenInfo *const token, goKind kind)
|
||||
{
|
||||
// ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
|
||||
// ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
|
||||
// IdentifierList = identifier { "," identifier } .
|
||||
// ExpressionList = Expression { "," Expression } .
|
||||
// TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
|
||||
// TypeSpec = identifier Type .
|
||||
// VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
|
||||
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
|
||||
tokenInfo *const name = newToken ();
|
||||
boolean usesParens = FALSE;
|
||||
|
||||
readToken (name);
|
||||
|
||||
if (isType (name, TOKEN_OPEN_PAREN))
|
||||
{
|
||||
usesParens = TRUE;
|
||||
readToken (name);
|
||||
}
|
||||
|
||||
again:
|
||||
while (1)
|
||||
{
|
||||
makeTag (name, kind);
|
||||
readToken (token);
|
||||
if (!isType (token, TOKEN_COMMA) && !isType (token, TOKEN_CLOSE_PAREN))
|
||||
break;
|
||||
readToken (name);
|
||||
}
|
||||
|
||||
skipType (token);
|
||||
skipToTopLevelSemicolon (token);
|
||||
|
||||
if (usesParens)
|
||||
{
|
||||
readToken (name);
|
||||
if (!isType (name, TOKEN_CLOSE_PAREN))
|
||||
goto again;
|
||||
}
|
||||
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseGoFile (tokenInfo *const token)
|
||||
{
|
||||
do
|
||||
{
|
||||
readToken (token);
|
||||
|
||||
if (isType (token, TOKEN_KEYWORD))
|
||||
{
|
||||
switch (token->keyword)
|
||||
{
|
||||
case KEYWORD_package:
|
||||
parsePackage (token);
|
||||
break;
|
||||
case KEYWORD_func:
|
||||
parseFunctionOrMethod (token);
|
||||
break;
|
||||
case KEYWORD_const:
|
||||
parseConstTypeVar (token, GOTAG_CONST);
|
||||
break;
|
||||
case KEYWORD_type:
|
||||
parseConstTypeVar (token, GOTAG_TYPE);
|
||||
break;
|
||||
case KEYWORD_var:
|
||||
parseConstTypeVar (token, GOTAG_VAR);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (TRUE);
|
||||
}
|
||||
|
||||
static void findGoTags (void)
|
||||
{
|
||||
tokenInfo *const token = newToken ();
|
||||
exception_t exception;
|
||||
|
||||
exception = (exception_t) (setjmp (Exception));
|
||||
while (exception == ExceptionNone)
|
||||
parseGoFile (token);
|
||||
|
||||
deleteToken (token);
|
||||
vStringDelete (scope);
|
||||
scope = NULL;
|
||||
}
|
||||
|
||||
extern parserDefinition *GoParser (void)
|
||||
{
|
||||
static const char *const extensions[] = { "go", NULL };
|
||||
parserDefinition *def = parserNew ("Go");
|
||||
def->kinds = GoKinds;
|
||||
def->kindCount = KIND_COUNT (GoKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findGoTags;
|
||||
def->initialize = initialize;
|
||||
return def;
|
||||
}
|
50
third_party/ctags/html.c
vendored
Normal file
50
third_party/ctags/html.c
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: html.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
#include "third_party/ctags/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: */
|
1691
third_party/ctags/jscript.c
vendored
Normal file
1691
third_party/ctags/jscript.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
260
third_party/ctags/keyword.c
vendored
Normal file
260
third_party/ctags/keyword.c
vendored
Normal file
|
@ -0,0 +1,260 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: keyword.c 715 2009-07-06 03:31:00Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/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 *entry = getHashTableEntry (hashedValue);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
extern int analyzeToken (vString *const name, langType language)
|
||||
{
|
||||
vString *keyword = vStringNew ();
|
||||
int result;
|
||||
vStringCopyToLower (keyword, name);
|
||||
result = lookupKeyword (vStringValue (keyword), language);
|
||||
vStringDelete (keyword);
|
||||
return result;
|
||||
}
|
||||
|
||||
#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: */
|
35
third_party/ctags/keyword.h
vendored
Normal file
35
third_party/ctags/keyword.h
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: keyword.h 658 2008-04-20 23:21:35Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "third_party/ctags/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
|
||||
extern int analyzeToken (vString *const name, langType language);
|
||||
|
||||
#endif /* _KEYWORD_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
140
third_party/ctags/lisp.c
vendored
Normal file
140
third_party/ctags/lisp.c
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: lisp.c 717 2009-07-07 03:40:50Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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", 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: */
|
713
third_party/ctags/lregex.c
vendored
Normal file
713
third_party/ctags/lregex.c
vendored
Normal file
|
@ -0,0 +1,713 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: lregex.c 747 2009-11-06 02:33:37Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#ifdef HAVE_REGCOMP
|
||||
#include "libc/str/str.h"
|
||||
|
||||
# ifdef HAVE_SYS_TYPES_H
|
||||
#include "libc/calls/makedev.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/calls/typedef/u.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/intrin/newbie.h"
|
||||
#include "libc/sock/select.h"
|
||||
#include "libc/sysv/consts/endian.h" /* declare off_t (not known to regex.h on FreeBSD) */
|
||||
# endif
|
||||
#include "third_party/regex/regex.h"
|
||||
#endif
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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)
|
||||
{
|
||||
regexPattern *p = &set->patterns [i];
|
||||
#if defined (POSIX_REGEX)
|
||||
regfree (p->pattern);
|
||||
#endif
|
||||
eFree (p->pattern);
|
||||
p->pattern = NULL;
|
||||
|
||||
if (p->type == PTRN_TAG)
|
||||
{
|
||||
eFree (p->u.tag.name_pattern);
|
||||
p->u.tag.name_pattern = NULL;
|
||||
eFree (p->u.tag.kind.name);
|
||||
p->u.tag.kind.name = NULL;
|
||||
if (p->u.tag.kind.description != NULL)
|
||||
{
|
||||
eFree (p->u.tag.kind.description);
|
||||
p->u.tag.kind.description = 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, "regcomp %s: %s", regexp, 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, "%s", 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 \"%s\" in --%s option", (dash + 1), 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 __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)
|
||||
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: */
|
135
third_party/ctags/lua.c
vendored
Normal file
135
third_party/ctags/lua.c
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: lua.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* Copyright (c) 2000-2001, Max Ischenko <mfi@ukr.net>.
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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: */
|
393
third_party/ctags/main.c
vendored
Normal file
393
third_party/ctags/main.c
vendored
Normal file
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* $Id: main.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* Copyright (c) 1996-2003, Darren Hiebert
|
||||
*
|
||||
* Author: Darren Hiebert <dhiebert@users.sourceforge.net>
|
||||
* 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 "libc/runtime/runtime.h"
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
/**/
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/main.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define plural(value) (((unsigned long)(value) == 1L) ? "" : "s")
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static struct { long files, lines, bytes; } Totals = { 0, 0, 0 };
|
||||
|
||||
/*
|
||||
* 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
|
||||
|| strcmp (Option.tagFileName, "/dev/stdout") == 0
|
||||
)))
|
||||
toStdout = TRUE;
|
||||
return toStdout;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
resize = recurseUsingOpendir (dirName);
|
||||
}
|
||||
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);
|
||||
|
||||
eStatFree (status);
|
||||
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;
|
||||
|
||||
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: */
|
40
third_party/ctags/main.h
vendored
Normal file
40
third_party/ctags/main.h
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: main.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.h"
|
||||
|
||||
#include "third_party/ctags/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: */
|
219
third_party/ctags/make.c
vendored
Normal file
219
third_party/ctags/make.c
vendored
Normal file
|
@ -0,0 +1,219 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: make.c 751 2010-02-27 17:41:57Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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' || c == EOF)
|
||||
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 {
|
||||
if (strcmp(vStringValue (name), "export") == 0 &&
|
||||
isIdentifier (c))
|
||||
{
|
||||
c = skipToNonWhite ();
|
||||
readIdentifier (c, name);
|
||||
}
|
||||
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", "GNUmakefile", 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: */
|
46
third_party/ctags/matlab.c
vendored
Normal file
46
third_party/ctags/matlab.c
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2008, David Fishburn
|
||||
*
|
||||
* 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 MATLAB language files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installMatLabRegex (const langType language)
|
||||
{
|
||||
/* function [x,y,z] = asdf */
|
||||
addTagRegex (language, "^function[ \t]*\\[.*\\][ \t]*=[ \t]*([a-zA-Z0-9_]+)", "\\1", "f,function", NULL);
|
||||
/* function x = asdf */
|
||||
addTagRegex (language, "^function[ \t]*[a-zA-Z0-9_]+[ \t]*=[ \t]*([a-zA-Z0-9_]+)", "\\1", "f,function", NULL);
|
||||
/* function asdf */
|
||||
addTagRegex (language, "^function[ \t]*([a-zA-Z0-9_]+)[^=]*$", "\\1", "f,function", NULL);
|
||||
}
|
||||
|
||||
extern parserDefinition* MatLabParser ()
|
||||
{
|
||||
static const char *const extensions [] = { "m", NULL };
|
||||
parserDefinition* const def = parserNew ("MatLab");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installMatLabRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
1149
third_party/ctags/objc.c
vendored
Normal file
1149
third_party/ctags/objc.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
1894
third_party/ctags/ocaml.c
vendored
Normal file
1894
third_party/ctags/ocaml.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
1847
third_party/ctags/options.c
vendored
Normal file
1847
third_party/ctags/options.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
155
third_party/ctags/options.h
vendored
Normal file
155
third_party/ctags/options.h
vendored
Normal file
|
@ -0,0 +1,155 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: options.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
|
||||
|
||||
#include "third_party/ctags/args.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/strlist.h"
|
||||
#include "third_party/ctags/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: */
|
672
third_party/ctags/parse.c
vendored
Normal file
672
third_party/ctags/parse.c
vendored
Normal file
|
@ -0,0 +1,672 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: parse.c 597 2007-07-31 05:35:30Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/main.h"
|
||||
#define OPTION_WRITE
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/parsers.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/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));
|
||||
if (result == LANG_IGNORE)
|
||||
result = getNamedLanguage (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)
|
||||
{
|
||||
def->parser = findRegexTags;
|
||||
accepted = TRUE;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 \"%s\" in \"%s\" option", vStringValue (langName), 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: */
|
130
third_party/ctags/parse.h
vendored
Normal file
130
third_party/ctags/parse.h
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: parse.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
#include "third_party/ctags/parsers.h" /* contains list of parsers */
|
||||
#include "third_party/ctags/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: */
|
66
third_party/ctags/parsers.h
vendored
Normal file
66
third_party/ctags/parsers.h
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: parsers.h 771 2010-11-30 13:15:12Z vberthoux $
|
||||
*
|
||||
* 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 \
|
||||
AntParser, \
|
||||
AsmParser, \
|
||||
AspParser, \
|
||||
AwkParser, \
|
||||
BasicParser, \
|
||||
BetaParser, \
|
||||
CParser, \
|
||||
CppParser, \
|
||||
CsharpParser, \
|
||||
CobolParser, \
|
||||
DosBatchParser, \
|
||||
EiffelParser, \
|
||||
ErlangParser, \
|
||||
FlexParser, \
|
||||
FortranParser, \
|
||||
GoParser, \
|
||||
HtmlParser, \
|
||||
JavaParser, \
|
||||
JavaScriptParser, \
|
||||
LispParser, \
|
||||
LuaParser, \
|
||||
MakefileParser, \
|
||||
MatLabParser, \
|
||||
ObjcParser , \
|
||||
OcamlParser, \
|
||||
PascalParser, \
|
||||
PerlParser, \
|
||||
PhpParser, \
|
||||
PythonParser, \
|
||||
RexxParser, \
|
||||
RubyParser, \
|
||||
SchemeParser, \
|
||||
ShParser, \
|
||||
SlangParser, \
|
||||
SmlParser, \
|
||||
SqlParser, \
|
||||
TclParser, \
|
||||
TexParser, \
|
||||
VeraParser, \
|
||||
VerilogParser, \
|
||||
VhdlParser, \
|
||||
VimParser, \
|
||||
YaccParser
|
||||
|
||||
#endif /* _PARSERS_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
269
third_party/ctags/pascal.c
vendored
Normal file
269
third_party/ctags/pascal.c
vendored
Normal file
|
@ -0,0 +1,269 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: pascal.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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 */
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
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: */
|
384
third_party/ctags/perl.c
vendored
Normal file
384
third_party/ctags/perl.c
vendored
Normal file
|
@ -0,0 +1,384 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: perl.c 601 2007-08-02 04:45:16Z perlguy0 $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
#define TRACE_PERL_C 0
|
||||
#define TRACE if (TRACE_PERL_C) printf("perl.c:%d: ", __LINE__), printf
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_NONE = -1,
|
||||
K_CONSTANT,
|
||||
K_FORMAT,
|
||||
K_LABEL,
|
||||
K_PACKAGE,
|
||||
K_SUBROUTINE,
|
||||
K_SUBROUTINE_DECLARATION
|
||||
} perlKind;
|
||||
|
||||
static kindOption PerlKinds [] = {
|
||||
{ TRUE, 'c', "constant", "constants" },
|
||||
{ TRUE, 'f', "format", "formats" },
|
||||
{ TRUE, 'l', "label", "labels" },
|
||||
{ TRUE, 'p', "package", "packages" },
|
||||
{ TRUE, 's', "subroutine", "subroutines" },
|
||||
{ FALSE, 'd', "subroutine declaration", "subroutine declarations" },
|
||||
};
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perl subroutine declaration may look like one of the following:
|
||||
*
|
||||
* sub abc;
|
||||
* sub abc :attr;
|
||||
* sub abc (proto);
|
||||
* sub abc (proto) :attr;
|
||||
*
|
||||
* Note that there may be more than one attribute. Attributes may
|
||||
* have things in parentheses (they look like arguments). Anything
|
||||
* inside of those parentheses goes. Prototypes may contain semi-colons.
|
||||
* The matching end when we encounter (outside of any parentheses) either
|
||||
* a semi-colon (that'd be a declaration) or an left curly brace
|
||||
* (definition).
|
||||
*
|
||||
* This is pretty complicated parsing (plus we all know that only perl can
|
||||
* parse Perl), so we are only promising best effort here.
|
||||
*
|
||||
* If we can't determine what this is (due to a file ending, for example),
|
||||
* we will return FALSE.
|
||||
*/
|
||||
static boolean isSubroutineDeclaration (const unsigned char *cp)
|
||||
{
|
||||
boolean attr = FALSE;
|
||||
int nparens = 0;
|
||||
|
||||
do {
|
||||
for ( ; *cp; ++cp) {
|
||||
SUB_DECL_SWITCH:
|
||||
switch (*cp) {
|
||||
case ':':
|
||||
if (nparens)
|
||||
break;
|
||||
else if (TRUE == attr)
|
||||
return FALSE; /* Invalid attribute name */
|
||||
else
|
||||
attr = TRUE;
|
||||
break;
|
||||
case '(':
|
||||
++nparens;
|
||||
break;
|
||||
case ')':
|
||||
--nparens;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
case ';':
|
||||
if (!nparens)
|
||||
return TRUE;
|
||||
case '{':
|
||||
if (!nparens)
|
||||
return FALSE;
|
||||
default:
|
||||
if (attr) {
|
||||
if (isIdentifier1(*cp)) {
|
||||
cp++;
|
||||
while (isIdentifier (*cp))
|
||||
cp++;
|
||||
attr = FALSE;
|
||||
goto SUB_DECL_SWITCH; /* Instead of --cp; */
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
} else if (nparens) {
|
||||
break;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (NULL != (cp = fileReadLine ()));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
tagEntryInfo e;
|
||||
|
||||
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)
|
||||
{
|
||||
TRACE("this looks like a sub\n");
|
||||
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)
|
||||
{
|
||||
/* This will point to space after 'package' so that a tag
|
||||
can be made */
|
||||
const unsigned char *space = 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, "::");
|
||||
|
||||
cp = space; /* Rewind */
|
||||
kind = K_PACKAGE;
|
||||
spaceRequired = TRUE;
|
||||
qualified = TRUE;
|
||||
}
|
||||
else if (strncmp((const char*) cp, "format", (size_t) 6) == 0)
|
||||
{
|
||||
cp += 6;
|
||||
kind = K_FORMAT;
|
||||
spaceRequired = TRUE;
|
||||
qualified = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isIdentifier1 (*cp))
|
||||
{
|
||||
const unsigned char *p = cp;
|
||||
while (isIdentifier (*p))
|
||||
++p;
|
||||
while (isspace (*p))
|
||||
++p;
|
||||
if ((int) *p == ':' && (int) *(p + 1) != ':')
|
||||
kind = K_LABEL;
|
||||
}
|
||||
}
|
||||
if (kind != K_NONE)
|
||||
{
|
||||
TRACE("cp0: %s\n", (const char *) cp);
|
||||
if (spaceRequired && *cp && !isspace (*cp))
|
||||
continue;
|
||||
|
||||
TRACE("cp1: %s\n", (const char *) cp);
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
|
||||
while (!*cp || '#' == *cp) { /* Gobble up empty lines
|
||||
and comments */
|
||||
cp = fileReadLine ();
|
||||
if (!cp)
|
||||
goto END_MAIN_WHILE;
|
||||
while (isspace (*cp))
|
||||
cp++;
|
||||
}
|
||||
|
||||
while (isIdentifier (*cp) || (K_PACKAGE == kind && ':' == *cp))
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (K_FORMAT == kind &&
|
||||
vStringLength (name) == 0 && /* cp did not advance */
|
||||
'=' == *cp)
|
||||
{
|
||||
/* format's name is optional. If it's omitted, 'STDOUT'
|
||||
is assumed. */
|
||||
vStringCatS (name, "STDOUT");
|
||||
}
|
||||
|
||||
vStringTerminate (name);
|
||||
TRACE("name: %s\n", name->buffer);
|
||||
|
||||
if (0 == vStringLength(name)) {
|
||||
vStringClear(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (K_SUBROUTINE == kind)
|
||||
{
|
||||
/*
|
||||
* isSubroutineDeclaration() may consume several lines. So
|
||||
* we record line positions.
|
||||
*/
|
||||
initTagEntry(&e, vStringValue(name));
|
||||
|
||||
if (TRUE == isSubroutineDeclaration(cp)) {
|
||||
if (TRUE == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) {
|
||||
kind = K_SUBROUTINE_DECLARATION;
|
||||
} else {
|
||||
vStringClear (name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
e.kind = PerlKinds[kind].letter;
|
||||
e.kindName = PerlKinds[kind].name;
|
||||
|
||||
makeTagEntry(&e);
|
||||
|
||||
if (Option.include.qualifiedTags && qualified &&
|
||||
package != NULL && vStringLength (package) > 0)
|
||||
{
|
||||
vString *const qualifiedName = vStringNew ();
|
||||
vStringCopy (qualifiedName, package);
|
||||
vStringCat (qualifiedName, name);
|
||||
e.name = vStringValue(qualifiedName);
|
||||
makeTagEntry(&e);
|
||||
vStringDelete (qualifiedName);
|
||||
}
|
||||
} else if (vStringLength (name) > 0)
|
||||
{
|
||||
makeSimpleTag (name, PerlKinds, kind);
|
||||
if (Option.include.qualifiedTags && qualified &&
|
||||
K_PACKAGE != kind &&
|
||||
package != NULL && vStringLength (package) > 0)
|
||||
{
|
||||
vString *const qualifiedName = vStringNew ();
|
||||
vStringCopy (qualifiedName, package);
|
||||
vStringCat (qualifiedName, name);
|
||||
makeSimpleTag (qualifiedName, PerlKinds, kind);
|
||||
vStringDelete (qualifiedName);
|
||||
}
|
||||
}
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
|
||||
END_MAIN_WHILE:
|
||||
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 noexpandtab: */
|
226
third_party/ctags/php.c
vendored
Normal file
226
third_party/ctags/php.c
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: php.c 734 2009-08-20 23:33:54Z jafl $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CLASS, K_DEFINE, K_FUNCTION, K_VARIABLE
|
||||
} phpKind;
|
||||
|
||||
#if 0
|
||||
static kindOption PhpKinds [] = {
|
||||
{ TRUE, 'c', "class", "classes" },
|
||||
{ TRUE, 'd', "define", "constant definitions" },
|
||||
{ TRUE, 'f', "function", "functions" },
|
||||
{ TRUE, 'v', "variable", "variables" }
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/* JavaScript patterns are duplicated in jscript.c */
|
||||
|
||||
#define ALPHA "[:alpha:]"
|
||||
#define ALNUM "[:alnum:]"
|
||||
|
||||
static void installPHPRegex (const langType language)
|
||||
{
|
||||
addTagRegex(language, "^[ \t]*((final|abstract)[ \t]+)*class[ \t]+([" ALPHA "_][" ALNUM "_]*)",
|
||||
"\\3", "c,class,classes", NULL);
|
||||
addTagRegex(language, "^[ \t]*interface[ \t]+([" ALPHA "_][" ALNUM "_]*)",
|
||||
"\\1", "i,interface,interfaces", NULL);
|
||||
addTagRegex(language, "^[ \t]*define[ \t]*\\([ \t]*['\"]?([" ALPHA "_][" ALNUM "_]*)",
|
||||
"\\1", "d,define,constant definitions", NULL);
|
||||
addTagRegex(language, "^[ \t]*((static|public|protected|private)[ \t]+)*function[ \t]+&?[ \t]*([" ALPHA "_][" ALNUM "_]*)",
|
||||
"\\3", "f,function,functions", NULL);
|
||||
addTagRegex(language, "^[ \t]*(\\$|::\\$|\\$this->)([" ALPHA "_][" ALNUM "_]*)[ \t]*=",
|
||||
"\\2", "v,variable,variables", NULL);
|
||||
addTagRegex(language, "^[ \t]*((var|public|protected|private|static)[ \t]+)+\\$([" ALPHA "_][" ALNUM "_]*)[ \t]*[=;]",
|
||||
"\\3", "v,variable,variables", NULL);
|
||||
|
||||
/* function regex is covered by PHP regex */
|
||||
addTagRegex (language, "(^|[ \t])([A-Za-z0-9_]+)[ \t]*[=:][ \t]*function[ \t]*\\(",
|
||||
"\\2", "j,jsfunction,javascript functions", NULL);
|
||||
addTagRegex (language, "(^|[ \t])([A-Za-z0-9_.]+)\\.([A-Za-z0-9_]+)[ \t]*=[ \t]*function[ \t]*\\(",
|
||||
"\\2.\\3", "j,jsfunction,javascript functions", NULL);
|
||||
addTagRegex (language, "(^|[ \t])([A-Za-z0-9_.]+)\\.([A-Za-z0-9_]+)[ \t]*=[ \t]*function[ \t]*\\(",
|
||||
"\\3", "j,jsfunction,javascript functions", NULL);
|
||||
}
|
||||
|
||||
/* Create parser definition structure */
|
||||
extern parserDefinition* PhpParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "php", "php3", "phtml", NULL };
|
||||
parserDefinition* def = parserNew ("PHP");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installPHPRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
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;
|
||||
const char* f;
|
||||
|
||||
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 ((f = strstr ((const char*) cp, "function")) != NULL &&
|
||||
(f == (const char*) cp || isspace ((int) f [-1])) &&
|
||||
isspace ((int) f [8]))
|
||||
{
|
||||
cp = ((const unsigned char *) f) + 8;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (*cp == '&') /* skip reference character and following whitespace */
|
||||
{
|
||||
cp++;
|
||||
|
||||
while (isspace ((int) *cp))
|
||||
++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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
776
third_party/ctags/python.c
vendored
Normal file
776
third_party/ctags/python.c
vendored
Normal file
|
@ -0,0 +1,776 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: python.c 752 2010-02-27 17:52:46Z elliotth $
|
||||
*
|
||||
* 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 "libc/mem/mem.h"
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/main.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/debug.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef struct NestingLevel NestingLevel;
|
||||
typedef struct NestingLevels NestingLevels;
|
||||
|
||||
struct NestingLevel
|
||||
{
|
||||
int indentation;
|
||||
vString *name;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct NestingLevels
|
||||
{
|
||||
NestingLevel *levels;
|
||||
int n; /* number of levels in use */
|
||||
int allocated;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
|
||||
} pythonKind;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static kindOption PythonKinds[] = {
|
||||
{TRUE, 'c', "class", "classes"},
|
||||
{TRUE, 'f', "function", "functions"},
|
||||
{TRUE, 'm', "member", "class members"},
|
||||
{TRUE, 'v', "variable", "variables"},
|
||||
{FALSE, 'i', "namespace", "imports"}
|
||||
};
|
||||
|
||||
static char const * const singletriple = "'''";
|
||||
static char const * const doubletriple = "\"\"\"";
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static NestingLevels *nestingLevelsNew (void)
|
||||
{
|
||||
NestingLevels *nls = xCalloc (1, NestingLevels);
|
||||
return nls;
|
||||
}
|
||||
|
||||
static void nestingLevelsFree (NestingLevels *nls)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nls->allocated; i++)
|
||||
vStringDelete(nls->levels[i].name);
|
||||
if (nls->levels) eFree(nls->levels);
|
||||
eFree(nls);
|
||||
}
|
||||
|
||||
static void nestingLevelsPush (NestingLevels *nls,
|
||||
const vString *name, int type)
|
||||
{
|
||||
NestingLevel *nl = NULL;
|
||||
|
||||
if (nls->n >= nls->allocated)
|
||||
{
|
||||
nls->allocated++;
|
||||
nls->levels = xRealloc(nls->levels,
|
||||
nls->allocated, NestingLevel);
|
||||
nls->levels[nls->n].name = vStringNew();
|
||||
}
|
||||
nl = &nls->levels[nls->n];
|
||||
nls->n++;
|
||||
|
||||
vStringCopy(nl->name, name);
|
||||
nl->type = type;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static NestingLevel *nestingLevelsGetCurrent (NestingLevels *nls)
|
||||
{
|
||||
Assert (nls != NULL);
|
||||
|
||||
if (nls->n < 1)
|
||||
return NULL;
|
||||
|
||||
return &nls->levels[nls->n - 1];
|
||||
}
|
||||
|
||||
static void nestingLevelsPop (NestingLevels *nls)
|
||||
{
|
||||
const NestingLevel *nl = nestingLevelsGetCurrent(nls);
|
||||
|
||||
Assert (nl != NULL);
|
||||
vStringClear(nl->name);
|
||||
nls->n--;
|
||||
}
|
||||
#endif
|
||||
|
||||
static boolean isIdentifierFirstCharacter (int c)
|
||||
{
|
||||
return (boolean) (isalpha (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isIdentifierCharacter (int c)
|
||||
{
|
||||
return (boolean) (isalnum (c) || c == '_');
|
||||
}
|
||||
|
||||
/* Given a string with the contents of a line directly after the "def" keyword,
|
||||
* extract all relevant information and create a tag.
|
||||
*/
|
||||
static void makeFunctionTag (vString *const function,
|
||||
vString *const parent, int is_class_parent, const char *arglist __unused)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (function));
|
||||
|
||||
tag.kindName = "function";
|
||||
tag.kind = 'f';
|
||||
/* tag.extensionFields.arglist = arglist; */
|
||||
|
||||
if (vStringLength (parent) > 0)
|
||||
{
|
||||
if (is_class_parent)
|
||||
{
|
||||
tag.kindName = "member";
|
||||
tag.kind = 'm';
|
||||
tag.extensionFields.scope [0] = "class";
|
||||
tag.extensionFields.scope [1] = vStringValue (parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
tag.extensionFields.scope [0] = "function";
|
||||
tag.extensionFields.scope [1] = vStringValue (parent);
|
||||
}
|
||||
}
|
||||
|
||||
/* If a function starts with __, we mark it as file scope.
|
||||
* FIXME: What is the proper way to signal such attributes?
|
||||
* TODO: What does functions/classes starting with _ and __ mean in python?
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/* Given a string with the contents of the line directly after the "class"
|
||||
* keyword, extract all necessary information and create a tag.
|
||||
*/
|
||||
static void makeClassTag (vString *const class, vString *const inheritance,
|
||||
vString *const parent, int is_class_parent)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (class));
|
||||
tag.kindName = "class";
|
||||
tag.kind = 'c';
|
||||
if (vStringLength (parent) > 0)
|
||||
{
|
||||
if (is_class_parent)
|
||||
{
|
||||
tag.extensionFields.scope [0] = "class";
|
||||
tag.extensionFields.scope [1] = vStringValue (parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
tag.extensionFields.scope [0] = "function";
|
||||
tag.extensionFields.scope [1] = vStringValue (parent);
|
||||
}
|
||||
}
|
||||
tag.extensionFields.inheritance = vStringValue (inheritance);
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
|
||||
static void makeVariableTag (vString *const var, vString *const parent)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (var));
|
||||
tag.kindName = "variable";
|
||||
tag.kind = 'v';
|
||||
if (vStringLength (parent) > 0)
|
||||
{
|
||||
tag.extensionFields.scope [0] = "class";
|
||||
tag.extensionFields.scope [1] = vStringValue (parent);
|
||||
}
|
||||
makeTagEntry (&tag);
|
||||
}
|
||||
|
||||
/* Skip a single or double quoted string. */
|
||||
static const char *skipString (const char *cp)
|
||||
{
|
||||
const char *start = cp;
|
||||
int escaped = 0;
|
||||
for (cp++; *cp; cp++)
|
||||
{
|
||||
if (escaped)
|
||||
escaped--;
|
||||
else if (*cp == '\\')
|
||||
escaped++;
|
||||
else if (*cp == *start)
|
||||
return cp + 1;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* Skip everything up to an identifier start. */
|
||||
static const char *skipEverything (const char *cp)
|
||||
{
|
||||
for (; *cp; cp++)
|
||||
{
|
||||
if (*cp == '"' || *cp == '\'' || *cp == '#')
|
||||
{
|
||||
cp = skipString(cp);
|
||||
if (!*cp) break;
|
||||
}
|
||||
if (isIdentifierFirstCharacter ((int) *cp))
|
||||
return cp;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* Skip an identifier. */
|
||||
static const char *skipIdentifier (const char *cp)
|
||||
{
|
||||
while (isIdentifierCharacter ((int) *cp))
|
||||
cp++;
|
||||
return cp;
|
||||
}
|
||||
|
||||
static const char *findDefinitionOrClass (const char *cp)
|
||||
{
|
||||
while (*cp)
|
||||
{
|
||||
cp = skipEverything (cp);
|
||||
if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5) ||
|
||||
!strncmp(cp, "cdef", 4) || !strncmp(cp, "cpdef", 5))
|
||||
{
|
||||
return cp;
|
||||
}
|
||||
cp = skipIdentifier (cp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *skipSpace (const char *cp)
|
||||
{
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* Starting at ''cp'', parse an identifier into ''identifier''. */
|
||||
static const char *parseIdentifier (const char *cp, vString *const identifier)
|
||||
{
|
||||
vStringClear (identifier);
|
||||
while (isIdentifierCharacter ((int) *cp))
|
||||
{
|
||||
vStringPut (identifier, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (identifier);
|
||||
return cp;
|
||||
}
|
||||
|
||||
static void parseClass (const char *cp, vString *const class,
|
||||
vString *const parent, int is_class_parent)
|
||||
{
|
||||
vString *const inheritance = vStringNew ();
|
||||
vStringClear (inheritance);
|
||||
cp = parseIdentifier (cp, class);
|
||||
cp = skipSpace (cp);
|
||||
if (*cp == '(')
|
||||
{
|
||||
++cp;
|
||||
while (*cp != ')')
|
||||
{
|
||||
if (*cp == '\0')
|
||||
{
|
||||
/* Closing parenthesis can be in follow up line. */
|
||||
cp = (const char *) fileReadLine ();
|
||||
if (!cp) break;
|
||||
vStringPut (inheritance, ' ');
|
||||
continue;
|
||||
}
|
||||
vStringPut (inheritance, *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (inheritance);
|
||||
}
|
||||
makeClassTag (class, inheritance, parent, is_class_parent);
|
||||
vStringDelete (inheritance);
|
||||
}
|
||||
|
||||
static void parseImports (const char *cp)
|
||||
{
|
||||
const char *pos;
|
||||
vString *name, *name_next;
|
||||
|
||||
cp = skipEverything (cp);
|
||||
|
||||
if ((pos = strstr (cp, "import")) == NULL)
|
||||
return;
|
||||
|
||||
cp = pos + 6;
|
||||
|
||||
/* continue only if there is some space between the keyword and the identifier */
|
||||
if (! isspace (*cp))
|
||||
return;
|
||||
|
||||
cp++;
|
||||
cp = skipSpace (cp);
|
||||
|
||||
name = vStringNew ();
|
||||
name_next = vStringNew ();
|
||||
|
||||
cp = skipEverything (cp);
|
||||
while (*cp)
|
||||
{
|
||||
cp = parseIdentifier (cp, name);
|
||||
|
||||
cp = skipEverything (cp);
|
||||
/* we parse the next possible import statement as well to be able to ignore 'foo' in
|
||||
* 'import foo as bar' */
|
||||
parseIdentifier (cp, name_next);
|
||||
|
||||
/* take the current tag only if the next one is not "as" */
|
||||
if (strcmp (vStringValue (name_next), "as") != 0 &&
|
||||
strcmp (vStringValue (name), "as") != 0)
|
||||
{
|
||||
makeSimpleTag (name, PythonKinds, K_IMPORT);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
vStringDelete (name_next);
|
||||
}
|
||||
|
||||
/* modified from get.c getArglistFromStr().
|
||||
* warning: terminates rest of string past arglist!
|
||||
* note: does not ignore brackets inside strings! */
|
||||
static char *parseArglist(const char *buf)
|
||||
{
|
||||
char *start, *end;
|
||||
int level;
|
||||
if (NULL == buf)
|
||||
return NULL;
|
||||
if (NULL == (start = strchr(buf, '(')))
|
||||
return NULL;
|
||||
for (level = 1, end = start + 1; level > 0; ++end)
|
||||
{
|
||||
if ('\0' == *end)
|
||||
break;
|
||||
else if ('(' == *end)
|
||||
++ level;
|
||||
else if (')' == *end)
|
||||
-- level;
|
||||
}
|
||||
*end = '\0';
|
||||
return strdup(start);
|
||||
}
|
||||
|
||||
static void parseFunction (const char *cp, vString *const def,
|
||||
vString *const parent, int is_class_parent)
|
||||
{
|
||||
char *arglist;
|
||||
|
||||
cp = parseIdentifier (cp, def);
|
||||
arglist = parseArglist (cp);
|
||||
makeFunctionTag (def, parent, is_class_parent, arglist);
|
||||
if (arglist != NULL) {
|
||||
eFree (arglist);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the combined name of a nested symbol. Classes are separated with ".",
|
||||
* functions with "/". For example this code:
|
||||
* class MyClass:
|
||||
* def myFunction:
|
||||
* def SubFunction:
|
||||
* class SubClass:
|
||||
* def Method:
|
||||
* pass
|
||||
* Would produce this string:
|
||||
* MyClass.MyFunction/SubFunction/SubClass.Method
|
||||
*/
|
||||
static boolean constructParentString(NestingLevels *nls, int indent,
|
||||
vString *result)
|
||||
{
|
||||
int i;
|
||||
NestingLevel *prev = NULL;
|
||||
int is_class = FALSE;
|
||||
vStringClear (result);
|
||||
for (i = 0; i < nls->n; i++)
|
||||
{
|
||||
NestingLevel *nl = nls->levels + i;
|
||||
if (indent <= nl->indentation)
|
||||
break;
|
||||
if (prev)
|
||||
{
|
||||
vStringCatS(result, "."); /* make Geany symbol list grouping work properly */
|
||||
/*
|
||||
if (prev->type == K_CLASS)
|
||||
vStringCatS(result, ".");
|
||||
else
|
||||
vStringCatS(result, "/");
|
||||
*/
|
||||
}
|
||||
vStringCat(result, nl->name);
|
||||
is_class = (nl->type == K_CLASS);
|
||||
prev = nl;
|
||||
}
|
||||
return is_class;
|
||||
}
|
||||
|
||||
/* Check whether parent's indentation level is higher than the current level and
|
||||
* if so, remove it.
|
||||
*/
|
||||
static void checkParent(NestingLevels *nls, int indent, vString *parent)
|
||||
{
|
||||
int i;
|
||||
NestingLevel *n;
|
||||
|
||||
for (i = 0; i < nls->n; i++)
|
||||
{
|
||||
n = nls->levels + i;
|
||||
/* is there a better way to compare two vStrings? */
|
||||
if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
|
||||
{
|
||||
if (n && indent <= n->indentation)
|
||||
{
|
||||
/* remove this level by clearing its name */
|
||||
vStringClear(n->name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addNestingLevel(NestingLevels *nls, int indentation,
|
||||
const vString *name, boolean is_class)
|
||||
{
|
||||
int i;
|
||||
NestingLevel *nl = NULL;
|
||||
|
||||
for (i = 0; i < nls->n; i++)
|
||||
{
|
||||
nl = nls->levels + i;
|
||||
if (indentation <= nl->indentation) break;
|
||||
}
|
||||
if (i == nls->n)
|
||||
{
|
||||
nestingLevelsPush(nls, name, 0);
|
||||
nl = nls->levels + i;
|
||||
}
|
||||
else
|
||||
{ /* reuse existing slot */
|
||||
nls->n = i + 1;
|
||||
vStringCopy(nl->name, name);
|
||||
}
|
||||
nl->indentation = indentation;
|
||||
nl->type = is_class ? K_CLASS : !K_CLASS;
|
||||
}
|
||||
|
||||
/* Return a pointer to the start of the next triple string, or NULL. Store
|
||||
* the kind of triple string in "which" if the return is not NULL.
|
||||
*/
|
||||
static char const *find_triple_start(char const *string, char const **which)
|
||||
{
|
||||
char const *cp = string;
|
||||
|
||||
for (; *cp; cp++)
|
||||
{
|
||||
if (*cp == '"' || *cp == '\'')
|
||||
{
|
||||
if (strncmp(cp, doubletriple, 3) == 0)
|
||||
{
|
||||
*which = doubletriple;
|
||||
return cp;
|
||||
}
|
||||
if (strncmp(cp, singletriple, 3) == 0)
|
||||
{
|
||||
*which = singletriple;
|
||||
return cp;
|
||||
}
|
||||
cp = skipString(cp);
|
||||
if (!*cp) break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the end of a triple string as pointed to by "which", and update "which"
|
||||
* with any other triple strings following in the given string.
|
||||
*/
|
||||
static void find_triple_end(char const *string, char const **which)
|
||||
{
|
||||
char const *s = string;
|
||||
while (1)
|
||||
{
|
||||
/* Check if the string ends in the same line. */
|
||||
s = strstr (s, *which);
|
||||
if (!s) break;
|
||||
s += 3;
|
||||
*which = NULL;
|
||||
/* If yes, check if another one starts in the same line. */
|
||||
s = find_triple_start(s, which);
|
||||
if (!s) break;
|
||||
s += 3;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *findVariable(const char *line)
|
||||
{
|
||||
/* Parse global and class variable names (C.x) from assignment statements.
|
||||
* Object attributes (obj.x) are ignored.
|
||||
* Assignment to a tuple 'x, y = 2, 3' not supported.
|
||||
* TODO: ignore duplicate tags from reassignment statements. */
|
||||
const char *cp, *sp, *eq, *start;
|
||||
|
||||
cp = strstr(line, "=");
|
||||
if (!cp)
|
||||
return NULL;
|
||||
eq = cp + 1;
|
||||
while (*eq)
|
||||
{
|
||||
if (*eq == '=')
|
||||
return NULL; /* ignore '==' operator and 'x=5,y=6)' function lines */
|
||||
if (*eq == '(' || *eq == '#')
|
||||
break; /* allow 'x = func(b=2,y=2,' lines and comments at the end of line */
|
||||
eq++;
|
||||
}
|
||||
|
||||
/* go backwards to the start of the line, checking we have valid chars */
|
||||
start = cp - 1;
|
||||
while (start >= line && isspace ((int) *start))
|
||||
--start;
|
||||
while (start >= line && isIdentifierCharacter ((int) *start))
|
||||
--start;
|
||||
if (!isIdentifierFirstCharacter(*(start + 1)))
|
||||
return NULL;
|
||||
sp = start;
|
||||
while (sp >= line && isspace ((int) *sp))
|
||||
--sp;
|
||||
if ((sp + 1) != line) /* the line isn't a simple variable assignment */
|
||||
return NULL;
|
||||
/* the line is valid, parse the variable name */
|
||||
++start;
|
||||
return start;
|
||||
}
|
||||
|
||||
/* Skip type declaration that optionally follows a cdef/cpdef */
|
||||
static const char *skipTypeDecl (const char *cp, boolean *is_class)
|
||||
{
|
||||
const char *lastStart = cp, *ptr = cp;
|
||||
int loopCount = 0;
|
||||
ptr = skipSpace(cp);
|
||||
if (!strncmp("extern", ptr, 6)) {
|
||||
ptr += 6;
|
||||
ptr = skipSpace(ptr);
|
||||
if (!strncmp("from", ptr, 4)) { return NULL; }
|
||||
}
|
||||
if (!strncmp("class", ptr, 5)) {
|
||||
ptr += 5 ;
|
||||
*is_class = TRUE;
|
||||
ptr = skipSpace(ptr);
|
||||
return ptr;
|
||||
}
|
||||
/* limit so that we don't pick off "int item=obj()" */
|
||||
while (*ptr && loopCount++ < 2) {
|
||||
while (*ptr && *ptr != '=' && *ptr != '(' && !isspace(*ptr)) ptr++;
|
||||
if (!*ptr || *ptr == '=') return NULL;
|
||||
if (*ptr == '(') {
|
||||
return lastStart; /* if we stopped on a '(' we are done */
|
||||
}
|
||||
ptr = skipSpace(ptr);
|
||||
lastStart = ptr;
|
||||
while (*lastStart == '*') lastStart++; /* cdef int *identifier */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void findPythonTags (void)
|
||||
{
|
||||
vString *const continuation = vStringNew ();
|
||||
vString *const name = vStringNew ();
|
||||
vString *const parent = vStringNew();
|
||||
|
||||
NestingLevels *const nesting_levels = nestingLevelsNew();
|
||||
|
||||
const char *line;
|
||||
int line_skip = 0;
|
||||
char const *longStringLiteral = NULL;
|
||||
|
||||
while ((line = (const char *) fileReadLine ()) != NULL)
|
||||
{
|
||||
const char *cp = line, *candidate;
|
||||
char const *longstring;
|
||||
char const *keyword, *variable;
|
||||
int indent;
|
||||
|
||||
cp = skipSpace (cp);
|
||||
|
||||
if (*cp == '\0') /* skip blank line */
|
||||
continue;
|
||||
|
||||
/* Skip comment if we are not inside a multi-line string. */
|
||||
if (*cp == '#' && !longStringLiteral)
|
||||
continue;
|
||||
|
||||
/* Deal with line continuation. */
|
||||
if (!line_skip) vStringClear(continuation);
|
||||
vStringCatS(continuation, line);
|
||||
vStringStripTrailing(continuation);
|
||||
if (vStringLast(continuation) == '\\')
|
||||
{
|
||||
vStringChop(continuation);
|
||||
vStringCatS(continuation, " ");
|
||||
line_skip = 1;
|
||||
continue;
|
||||
}
|
||||
cp = line = vStringValue(continuation);
|
||||
cp = skipSpace (cp);
|
||||
indent = cp - line;
|
||||
line_skip = 0;
|
||||
|
||||
checkParent(nesting_levels, indent, parent);
|
||||
|
||||
/* Deal with multiline string ending. */
|
||||
if (longStringLiteral)
|
||||
{
|
||||
find_triple_end(cp, &longStringLiteral);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Deal with multiline string start. */
|
||||
longstring = find_triple_start(cp, &longStringLiteral);
|
||||
if (longstring)
|
||||
{
|
||||
longstring += 3;
|
||||
find_triple_end(longstring, &longStringLiteral);
|
||||
/* We don't parse for any tags in the rest of the line. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Deal with def and class keywords. */
|
||||
keyword = findDefinitionOrClass (cp);
|
||||
if (keyword)
|
||||
{
|
||||
boolean found = FALSE;
|
||||
boolean is_class = FALSE;
|
||||
if (!strncmp (keyword, "def ", 4))
|
||||
{
|
||||
cp = skipSpace (keyword + 3);
|
||||
found = TRUE;
|
||||
}
|
||||
else if (!strncmp (keyword, "class ", 6))
|
||||
{
|
||||
cp = skipSpace (keyword + 5);
|
||||
found = TRUE;
|
||||
is_class = TRUE;
|
||||
}
|
||||
else if (!strncmp (keyword, "cdef ", 5))
|
||||
{
|
||||
cp = skipSpace(keyword + 4);
|
||||
candidate = skipTypeDecl (cp, &is_class);
|
||||
if (candidate)
|
||||
{
|
||||
found = TRUE;
|
||||
cp = candidate;
|
||||
}
|
||||
|
||||
}
|
||||
else if (!strncmp (keyword, "cpdef ", 6))
|
||||
{
|
||||
cp = skipSpace(keyword + 5);
|
||||
candidate = skipTypeDecl (cp, &is_class);
|
||||
if (candidate)
|
||||
{
|
||||
found = TRUE;
|
||||
cp = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
boolean is_parent_class;
|
||||
|
||||
is_parent_class =
|
||||
constructParentString(nesting_levels, indent, parent);
|
||||
|
||||
if (is_class)
|
||||
parseClass (cp, name, parent, is_parent_class);
|
||||
else
|
||||
parseFunction(cp, name, parent, is_parent_class);
|
||||
|
||||
addNestingLevel(nesting_levels, indent, name, is_class);
|
||||
}
|
||||
}
|
||||
/* Find global and class variables */
|
||||
variable = findVariable(line);
|
||||
if (variable)
|
||||
{
|
||||
const char *start = variable;
|
||||
boolean parent_is_class;
|
||||
|
||||
vStringClear (name);
|
||||
while (isIdentifierCharacter ((int) *start))
|
||||
{
|
||||
vStringPut (name, (int) *start);
|
||||
++start;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
|
||||
parent_is_class = constructParentString(nesting_levels, indent, parent);
|
||||
/* skip variables in methods */
|
||||
if (! parent_is_class && vStringLength(parent) > 0)
|
||||
continue;
|
||||
|
||||
makeVariableTag (name, parent);
|
||||
}
|
||||
/* Find and parse imports */
|
||||
parseImports(line);
|
||||
}
|
||||
/* Clean up all memory we allocated. */
|
||||
vStringDelete (parent);
|
||||
vStringDelete (name);
|
||||
vStringDelete (continuation);
|
||||
nestingLevelsFree (nesting_levels);
|
||||
}
|
||||
|
||||
extern parserDefinition *PythonParser (void)
|
||||
{
|
||||
static const char *const extensions[] = { "py", "pyx", "pxd", "pxi" ,"scons", 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: */
|
565
third_party/ctags/read.c
vendored
Normal file
565
third_party/ctags/read.c
vendored
Normal file
|
@ -0,0 +1,565 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: read.c 769 2010-09-11 21:00:16Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#define FILE_WRITE
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/main.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/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)
|
||||
{
|
||||
if (File.name != NULL)
|
||||
vStringDelete (File.name);
|
||||
if (File.path != NULL)
|
||||
vStringDelete (File.path);
|
||||
if (File.source.name != NULL)
|
||||
vStringDelete (File.source.name);
|
||||
if (File.source.tagPath != NULL)
|
||||
eFree (File.source.tagPath);
|
||||
if (File.line != NULL)
|
||||
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.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/Mac OS X), CR (Mac OS 9),
|
||||
* and CR-LF (MS-DOS) are converted into a generic newline.
|
||||
*/
|
||||
#ifndef macintosh
|
||||
const int next = getc (File.fp); /* is CR followed by LF? */
|
||||
if (next != NEWLINE)
|
||||
ungetc (next, File.fp);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
extern int fileSkipToCharacter (int c)
|
||||
{
|
||||
int d;
|
||||
do
|
||||
{
|
||||
d = fileGetc ();
|
||||
} while (d != EOF && d != c);
|
||||
return d;
|
||||
}
|
||||
|
||||
/* 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: */
|
123
third_party/ctags/read.h
vendored
Normal file
123
third_party/ctags/read.h
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: read.h 769 2010-09-11 21:00:16Z dhiebert $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/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? */
|
||||
|
||||
/* 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 int fileSkipToCharacter (int c);
|
||||
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: */
|
956
third_party/ctags/readtags.c
vendored
Normal file
956
third_party/ctags/readtags.c
vendored
Normal file
|
@ -0,0 +1,956 @@
|
|||
/*
|
||||
* $Id: readtags.c 592 2007-07-31 03:30:41Z dhiebert $
|
||||
*
|
||||
* Copyright (c) 1996-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released into the public domain.
|
||||
*
|
||||
* This module contains functions for reading tag files.
|
||||
*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/ctags/readtags.h"
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
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 = (unsigned short) 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);
|
||||
|
||||
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)
|
||||
{
|
||||
int fieldsPresent;
|
||||
*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 = strdup (str);
|
||||
if (result == NULL)
|
||||
perror (NULL);
|
||||
}
|
||||
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 = (short) 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*) calloc ((size_t) 1, sizeof (tagFile));
|
||||
if (result != NULL)
|
||||
{
|
||||
growString (&result->line);
|
||||
growString (&result->name);
|
||||
result->fields.max = 20;
|
||||
result->fields.list = (tagExtensionField*) calloc (
|
||||
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);
|
||||
if (file->search.name != NULL)
|
||||
free (file->search.name);
|
||||
|
||||
memset (file, 0, sizeof (tagFile));
|
||||
|
||||
free (file);
|
||||
}
|
||||
|
||||
static tagResult readNext (tagFile *const file, tagEntry *const entry)
|
||||
{
|
||||
tagResult result;
|
||||
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;
|
||||
if (file->search.name != NULL)
|
||||
free (file->search.name);
|
||||
file->search.name = duplicate (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;
|
||||
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: */
|
253
third_party/ctags/readtags.h
vendored
Normal file
253
third_party/ctags/readtags.h
vendored
Normal file
|
@ -0,0 +1,253 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: readtags.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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: */
|
40
third_party/ctags/rexx.c
vendored
Normal file
40
third_party/ctags/rexx.c
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: rexx.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* always include first */
|
||||
#include "third_party/ctags/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: */
|
533
third_party/ctags/routines.c
vendored
Normal file
533
third_party/ctags/routines.c
vendored
Normal file
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* $Id: routines.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h"
|
||||
/**/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "third_party/ctags/config.h"
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
* Miscellaneous macros
|
||||
*/
|
||||
#define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
|
||||
|
||||
char *CurrentDirectory;
|
||||
|
||||
static const char *ExecutableProgram;
|
||||
static const char *ExecutableName;
|
||||
|
||||
/*
|
||||
* 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 const char *getExecutablePath (void)
|
||||
{
|
||||
return ExecutableProgram;
|
||||
}
|
||||
|
||||
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))
|
||||
fprintf (errout, " : %s", strerror (errno));
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char* buf;
|
||||
if (CurrentDirectory == NULL)
|
||||
CurrentDirectory = xMalloc ((size_t) (PATH_MAX + 1), char);
|
||||
buf = getcwd (CurrentDirectory, PATH_MAX);
|
||||
if (buf == NULL)
|
||||
perror ("");
|
||||
if (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1] !=
|
||||
PATH_SEPARATOR)
|
||||
{
|
||||
sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c",
|
||||
OUTPUT_PATH_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
eStatFree (&file);
|
||||
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;
|
||||
file.isDirectory = (boolean) S_ISDIR (status.st_mode);
|
||||
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 void eStatFree (fileStatus *status)
|
||||
{
|
||||
if (status->name != NULL)
|
||||
{
|
||||
eFree (status->name);
|
||||
status->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pathname manipulation (O/S dependent!!!)
|
||||
*/
|
||||
|
||||
static boolean isPathSeparator (const int c)
|
||||
{
|
||||
boolean result;
|
||||
result = (boolean) (c == PATH_SEPARATOR);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern boolean isSameFile (const char *const name1, const char *const name2)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
struct stat stat1, stat2;
|
||||
if (stat (name1, &stat1) == 0 && stat (name2, &stat2) == 0)
|
||||
result = (boolean) (stat1.st_ino == stat2.st_ino);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern const char *baseFilename (const char *const filePath)
|
||||
{
|
||||
const char *tail = strrchr (filePath, PATH_SEPARATOR);
|
||||
if (tail == NULL)
|
||||
tail = filePath;
|
||||
else
|
||||
++tail; /* step past last delimiter */
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
||||
extern const char *fileExtension (const char *const fileName)
|
||||
{
|
||||
const char *extension;
|
||||
const char *pDelimiter = NULL;
|
||||
const char *const base = baseFilename (fileName);
|
||||
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;
|
||||
result = isPathSeparator (path [0]);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern vString *combinePathAndFile (
|
||||
const char *const path, const char *const file)
|
||||
{
|
||||
vString *const filePath = vStringNew ();
|
||||
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);
|
||||
|
||||
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))
|
||||
{
|
||||
res = eStrdup (file);
|
||||
}
|
||||
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 "/.." */
|
||||
memmove (cp, slashp + 3, strlen(slashp + 3) + 1);
|
||||
slashp = cp;
|
||||
continue;
|
||||
}
|
||||
else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0')
|
||||
{
|
||||
memmove (slashp, slashp + 2, strlen(slashp + 2) + 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
slashp = strchr (slashp + 1, PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
if (res [0] == '\0')
|
||||
return eStrdup ("/");
|
||||
else
|
||||
{
|
||||
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;
|
||||
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);
|
||||
eStatFree (file);
|
||||
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: */
|
137
third_party/ctags/routines.h
vendored
Normal file
137
third_party/ctags/routines.h
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: routines.h 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/vstring.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "third_party/ctags/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 const char *getExecutablePath (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 void eStatFree (fileStatus *status);
|
||||
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: */
|
410
third_party/ctags/ruby.c
vendored
Normal file
410
third_party/ctags/ruby.c
vendored
Normal file
|
@ -0,0 +1,410 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: ruby.c 571 2007-06-24 23:32:14Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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 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 == '"')
|
||||
{
|
||||
/* Skip string literals.
|
||||
* FIXME: should cope with escapes and interpolation.
|
||||
*/
|
||||
do {
|
||||
++cp;
|
||||
} while (*cp != 0 && *cp != '"');
|
||||
}
|
||||
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: */
|
113
third_party/ctags/scheme.c
vendored
Normal file
113
third_party/ctags/scheme.c
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: scheme.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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: */
|
117
third_party/ctags/sh.c
vendored
Normal file
117
third_party/ctags/sh.c
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: sh.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/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: */
|
42
third_party/ctags/slang.c
vendored
Normal file
42
third_party/ctags/slang.c
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: slang.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
#include "third_party/ctags/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;
|
||||
}
|
214
third_party/ctags/sml.c
vendored
Normal file
214
third_party/ctags/sml.c
vendored
Normal file
|
@ -0,0 +1,214 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: sml.c 536 2007-06-02 06:09:00Z elliotth $
|
||||
*
|
||||
* 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 "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/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;
|
||||
}
|
||||
vStringDelete (identifier);
|
||||
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: */
|
255
third_party/ctags/sort.c
vendored
Normal file
255
third_party/ctags/sort.c
vendored
Normal file
|
@ -0,0 +1,255 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: sort.c 747 2009-11-06 02:33:37Z dhiebert $
|
||||
*
|
||||
* 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 "libc/mem/mem.h"
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#if defined (HAVE_STDLIB_H)
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/musl/crypt.h"
|
||||
#include "third_party/musl/rand48.h" /* to declare malloc () */
|
||||
#endif
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/options.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/sort.h"
|
||||
|
||||
/*
|
||||
* 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, "%s", 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: */
|
33
third_party/ctags/sort.h
vendored
Normal file
33
third_party/ctags/sort.h
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: sort.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 "third_party/ctags/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: */
|
2379
third_party/ctags/sql.c
vendored
Normal file
2379
third_party/ctags/sql.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
283
third_party/ctags/strlist.c
vendored
Normal file
283
third_party/ctags/strlist.c
vendored
Normal file
|
@ -0,0 +1,283 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: strlist.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 managing resizable string lists.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#ifdef HAVE_FNMATCH_H
|
||||
#include "third_party/musl/fnmatch.h"
|
||||
#endif
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/strlist.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern stringList *stringListNew (void)
|
||||
{
|
||||
stringList* const result = xMalloc (1, stringList);
|
||||
result->max = 0;
|
||||
result->count = 0;
|
||||
result->list = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void stringListAdd (stringList *const current, vString *string)
|
||||
{
|
||||
enum { incrementalIncrease = 10 };
|
||||
Assert (current != NULL);
|
||||
if (current->list == NULL)
|
||||
{
|
||||
Assert (current->max == 0);
|
||||
current->count = 0;
|
||||
current->max = incrementalIncrease;
|
||||
current->list = xMalloc (current->max, vString*);
|
||||
}
|
||||
else if (current->count == current->max)
|
||||
{
|
||||
current->max += incrementalIncrease;
|
||||
current->list = xRealloc (current->list, current->max, vString*);
|
||||
}
|
||||
current->list [current->count++] = string;
|
||||
}
|
||||
|
||||
extern void stringListRemoveLast (stringList *const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
Assert (current->count > 0);
|
||||
--current->count;
|
||||
current->list [current->count] = NULL;
|
||||
}
|
||||
|
||||
/* Combine list `from' into `current', deleting `from' */
|
||||
extern void stringListCombine (
|
||||
stringList *const current, stringList *const from)
|
||||
{
|
||||
unsigned int i;
|
||||
Assert (current != NULL);
|
||||
Assert (from != NULL);
|
||||
for (i = 0 ; i < from->count ; ++i)
|
||||
{
|
||||
stringListAdd (current, from->list [i]);
|
||||
from->list [i] = NULL;
|
||||
}
|
||||
stringListDelete (from);
|
||||
}
|
||||
|
||||
extern stringList* stringListNewFromArgv (const char* const* const argv)
|
||||
{
|
||||
stringList* const result = stringListNew ();
|
||||
const char *const *p;
|
||||
Assert (argv != NULL);
|
||||
for (p = argv ; *p != NULL ; ++p)
|
||||
stringListAdd (result, vStringNewInit (*p));
|
||||
return result;
|
||||
}
|
||||
|
||||
extern stringList* stringListNewFromFile (const char* const fileName)
|
||||
{
|
||||
stringList* result = NULL;
|
||||
FILE* const fp = fopen (fileName, "r");
|
||||
if (fp != NULL)
|
||||
{
|
||||
result = stringListNew ();
|
||||
while (! feof (fp))
|
||||
{
|
||||
vString* const str = vStringNew ();
|
||||
readLine (str, fp);
|
||||
vStringStripTrailing (str);
|
||||
if (vStringLength (str) > 0)
|
||||
stringListAdd (result, str);
|
||||
else
|
||||
vStringDelete (str);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern unsigned int stringListCount (const stringList *const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
return current->count;
|
||||
}
|
||||
|
||||
extern vString* stringListItem (
|
||||
const stringList *const current, const unsigned int indx)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
return current->list [indx];
|
||||
}
|
||||
|
||||
extern vString* stringListLast (const stringList *const current)
|
||||
{
|
||||
Assert (current != NULL);
|
||||
Assert (current->count > 0);
|
||||
return current->list [current->count - 1];
|
||||
}
|
||||
|
||||
extern void stringListClear (stringList *const current)
|
||||
{
|
||||
unsigned int i;
|
||||
Assert (current != NULL);
|
||||
for (i = 0 ; i < current->count ; ++i)
|
||||
{
|
||||
vStringDelete (current->list [i]);
|
||||
current->list [i] = NULL;
|
||||
}
|
||||
current->count = 0;
|
||||
}
|
||||
|
||||
extern void stringListDelete (stringList *const current)
|
||||
{
|
||||
if (current != NULL)
|
||||
{
|
||||
if (current->list != NULL)
|
||||
{
|
||||
stringListClear (current);
|
||||
eFree (current->list);
|
||||
current->list = NULL;
|
||||
}
|
||||
current->max = 0;
|
||||
current->count = 0;
|
||||
eFree (current);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean compareString (
|
||||
const char *const string, vString *const itm)
|
||||
{
|
||||
return (boolean) (strcmp (string, vStringValue (itm)) == 0);
|
||||
}
|
||||
|
||||
static boolean compareStringInsensitive (
|
||||
const char *const string, vString *const itm)
|
||||
{
|
||||
return (boolean) (strcasecmp (string, vStringValue (itm)) == 0);
|
||||
}
|
||||
|
||||
static int stringListIndex (
|
||||
const stringList *const current,
|
||||
const char *const string,
|
||||
boolean (*test)(const char *s, vString *const vs))
|
||||
{
|
||||
int result = -1;
|
||||
unsigned int i;
|
||||
Assert (current != NULL);
|
||||
Assert (string != NULL);
|
||||
Assert (test != NULL);
|
||||
for (i = 0 ; result == -1 && i < current->count ; ++i)
|
||||
if ((*test)(string, current->list [i]))
|
||||
result = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
extern boolean stringListHas (
|
||||
const stringList *const current, const char *const string)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
Assert (current != NULL);
|
||||
result = stringListIndex (current, string, compareString) != -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
extern boolean stringListHasInsensitive (
|
||||
const stringList *const current, const char *const string)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
Assert (current != NULL);
|
||||
Assert (string != NULL);
|
||||
result = stringListIndex (current, string, compareStringInsensitive) != -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
extern boolean stringListHasTest (
|
||||
const stringList *const current, boolean (*test)(const char *s))
|
||||
{
|
||||
boolean result = FALSE;
|
||||
unsigned int i;
|
||||
Assert (current != NULL);
|
||||
for (i = 0 ; ! result && i < current->count ; ++i)
|
||||
result = (*test)(vStringValue (current->list [i]));
|
||||
return result;
|
||||
}
|
||||
|
||||
extern boolean stringListRemoveExtension (
|
||||
stringList* const current, const char* const extension)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
int where;
|
||||
#ifdef CASE_INSENSITIVE_FILENAMES
|
||||
where = stringListIndex (current, extension, compareStringInsensitive);
|
||||
#else
|
||||
where = stringListIndex (current, extension, compareString);
|
||||
#endif
|
||||
if (where != -1)
|
||||
{
|
||||
memmove (current->list + where, current->list + where + 1,
|
||||
(current->count - where) * sizeof (*current->list));
|
||||
current->list [current->count - 1] = NULL;
|
||||
--current->count;
|
||||
result = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern boolean stringListExtensionMatched (
|
||||
const stringList* const current, const char* const extension)
|
||||
{
|
||||
#ifdef CASE_INSENSITIVE_FILENAMES
|
||||
return stringListHasInsensitive (current, extension);
|
||||
#else
|
||||
return stringListHas (current, extension);
|
||||
#endif
|
||||
}
|
||||
|
||||
static boolean fileNameMatched (
|
||||
const vString* const vpattern, const char* const fileName)
|
||||
{
|
||||
const char* const pattern = vStringValue (vpattern);
|
||||
#if defined (HAVE_FNMATCH)
|
||||
return (boolean) (fnmatch (pattern, fileName, 0) == 0);
|
||||
#elif defined (CASE_INSENSITIVE_FILENAMES)
|
||||
return (boolean) (strcasecmp (pattern, fileName) == 0);
|
||||
#else
|
||||
return (boolean) (strcmp (pattern, fileName) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern boolean stringListFileMatched (
|
||||
const stringList* const current, const char* const fileName)
|
||||
{
|
||||
boolean result = FALSE;
|
||||
unsigned int i;
|
||||
for (i = 0 ; ! result && i < stringListCount (current) ; ++i)
|
||||
result = fileNameMatched (stringListItem (current, i), fileName);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern void stringListPrint (const stringList *const current)
|
||||
{
|
||||
unsigned int i;
|
||||
Assert (current != NULL);
|
||||
for (i = 0 ; i < current->count ; ++i)
|
||||
printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i]));
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
55
third_party/ctags/strlist.h
vendored
Normal file
55
third_party/ctags/strlist.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: strlist.h 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 resizable string lists.
|
||||
*/
|
||||
#ifndef _STRLIST_H
|
||||
#define _STRLIST_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef struct sStringList {
|
||||
unsigned int max;
|
||||
unsigned int count;
|
||||
vString **list;
|
||||
} stringList;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern stringList *stringListNew (void);
|
||||
extern void stringListAdd (stringList *const current, vString *string);
|
||||
extern void stringListRemoveLast (stringList *const current);
|
||||
extern void stringListCombine (stringList *const current, stringList *const from);
|
||||
extern stringList* stringListNewFromArgv (const char* const* const list);
|
||||
extern stringList* stringListNewFromFile (const char* const fileName);
|
||||
extern void stringListClear (stringList *const current);
|
||||
extern unsigned int stringListCount (const stringList *const current);
|
||||
extern vString* stringListItem (const stringList *const current, const unsigned int indx);
|
||||
extern vString* stringListLast (const stringList *const current);
|
||||
extern void stringListDelete (stringList *const current);
|
||||
extern boolean stringListHasInsensitive (const stringList *const current, const char *const string);
|
||||
extern boolean stringListHas (const stringList *const current, const char *const string);
|
||||
extern boolean stringListHasTest (const stringList *const current, boolean (*test)(const char *s));
|
||||
extern boolean stringListRemoveExtension (stringList* const current, const char* const extension);
|
||||
extern boolean stringListExtensionMatched (const stringList* const list, const char* const extension);
|
||||
extern boolean stringListFileMatched (const stringList* const list, const char* const str);
|
||||
extern void stringListPrint (const stringList *const current);
|
||||
|
||||
#endif /* _STRLIST_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
118
third_party/ctags/tcl.c
vendored
Normal file
118
third_party/ctags/tcl.c
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: tcl.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* 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 TCL scripts.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CLASS, K_METHOD, K_PROCEDURE
|
||||
} tclKind;
|
||||
|
||||
static kindOption TclKinds [] = {
|
||||
{ TRUE, 'c', "class", "classes" },
|
||||
{ TRUE, 'm', "method", "methods" },
|
||||
{ TRUE, 'p', "procedure", "procedures" }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static const unsigned char *makeTclTag (
|
||||
const unsigned char *cp,
|
||||
vString *const name,
|
||||
const tclKind kind)
|
||||
{
|
||||
vStringClear (name);
|
||||
while ((int) *cp != '\0' && ! isspace ((int) *cp))
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
}
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, TclKinds, kind);
|
||||
return cp;
|
||||
}
|
||||
|
||||
static boolean match (const unsigned char *line, const char *word)
|
||||
{
|
||||
return (boolean) (strncmp ((const char*) line, word, strlen (word)) == 0);
|
||||
}
|
||||
|
||||
static void findTclTags (void)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
const unsigned char *cp;
|
||||
|
||||
while (isspace (line [0]))
|
||||
++line;
|
||||
|
||||
if (line [0] == '\0' || line [0] == '#')
|
||||
continue;
|
||||
|
||||
/* read first word */
|
||||
for (cp = line ; *cp != '\0' && ! isspace ((int) *cp) ; ++cp)
|
||||
;
|
||||
if (! isspace ((int) *cp))
|
||||
continue;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
/* Now `line' points at first word and `cp' points at next word */
|
||||
|
||||
if (match (line, "proc"))
|
||||
cp = makeTclTag (cp, name, K_PROCEDURE);
|
||||
else if (match (line, "class") || match (line, "itcl::class"))
|
||||
cp = makeTclTag (cp, name, K_CLASS);
|
||||
else if (match (line, "public") ||
|
||||
match (line, "protected") ||
|
||||
match (line, "private"))
|
||||
{
|
||||
if (match (cp, "method"))
|
||||
{
|
||||
cp += 6;
|
||||
while (isspace ((int) *cp))
|
||||
++cp;
|
||||
cp = makeTclTag (cp, name, K_METHOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* TclParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "tcl", "tk", "wish", "itcl", NULL };
|
||||
parserDefinition* def = parserNew ("Tcl");
|
||||
def->kinds = TclKinds;
|
||||
def->kindCount = KIND_COUNT (TclKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findTclTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
523
third_party/ctags/tex.c
vendored
Normal file
523
third_party/ctags/tex.c
vendored
Normal file
|
@ -0,0 +1,523 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: tex.c 666 2008-05-15 17:47:31Z dfishburn $
|
||||
*
|
||||
* Copyright (c) 2008, David Fishburn
|
||||
*
|
||||
* 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 TeX language files.
|
||||
*
|
||||
* Tex language reference:
|
||||
* http://en.wikibooks.org/wiki/TeX#The_Structure_of_TeX
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
#include "libc/str/str.h" /* to define isalpha () */
|
||||
#include "libc/runtime/runtime.h"
|
||||
#ifdef DEBUG
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.h"
|
||||
#endif
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define isType(token,t) (boolean) ((token)->type == (t))
|
||||
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
||||
|
||||
/*
|
||||
* Used to specify type of keyword.
|
||||
*/
|
||||
typedef enum eKeywordId {
|
||||
KEYWORD_NONE = -1,
|
||||
KEYWORD_chapter,
|
||||
KEYWORD_section,
|
||||
KEYWORD_subsection,
|
||||
KEYWORD_subsubsection,
|
||||
KEYWORD_part,
|
||||
KEYWORD_paragraph,
|
||||
KEYWORD_subparagraph,
|
||||
KEYWORD_include
|
||||
} keywordId;
|
||||
|
||||
/* Used to determine whether keyword is valid for the token language and
|
||||
* what its ID is.
|
||||
*/
|
||||
typedef struct sKeywordDesc {
|
||||
const char *name;
|
||||
keywordId id;
|
||||
} keywordDesc;
|
||||
|
||||
typedef enum eTokenType {
|
||||
TOKEN_UNDEFINED,
|
||||
TOKEN_CHARACTER,
|
||||
TOKEN_CLOSE_PAREN,
|
||||
TOKEN_COMMA,
|
||||
TOKEN_KEYWORD,
|
||||
TOKEN_OPEN_PAREN,
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_STRING,
|
||||
TOKEN_OPEN_CURLY,
|
||||
TOKEN_CLOSE_CURLY,
|
||||
TOKEN_OPEN_SQUARE,
|
||||
TOKEN_CLOSE_SQUARE,
|
||||
TOKEN_QUESTION_MARK,
|
||||
TOKEN_STAR
|
||||
} tokenType;
|
||||
|
||||
typedef struct sTokenInfo {
|
||||
tokenType type;
|
||||
keywordId keyword;
|
||||
vString * string;
|
||||
vString * scope;
|
||||
unsigned long lineNumber;
|
||||
fpos_t filePosition;
|
||||
} tokenInfo;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
|
||||
static langType Lang_js;
|
||||
|
||||
static jmp_buf Exception;
|
||||
|
||||
typedef enum {
|
||||
TEXTAG_CHAPTER,
|
||||
TEXTAG_SECTION,
|
||||
TEXTAG_SUBSECTION,
|
||||
TEXTAG_SUBSUBSECTION,
|
||||
TEXTAG_PART,
|
||||
TEXTAG_PARAGRAPH,
|
||||
TEXTAG_SUBPARAGRAPH,
|
||||
TEXTAG_INCLUDE,
|
||||
TEXTAG_COUNT
|
||||
} texKind;
|
||||
|
||||
static kindOption TexKinds [] = {
|
||||
{ TRUE, 'c', "chapter", "chapters" },
|
||||
{ TRUE, 's', "section", "sections" },
|
||||
{ TRUE, 'u', "subsection", "subsections" },
|
||||
{ TRUE, 'b', "subsubsection", "subsubsections" },
|
||||
{ TRUE, 'p', "part", "parts" },
|
||||
{ TRUE, 'P', "paragraph", "paragraphs" },
|
||||
{ TRUE, 'G', "subparagraph", "subparagraphs" },
|
||||
{ TRUE, 'i', "include", "includes" }
|
||||
};
|
||||
|
||||
static const keywordDesc TexKeywordTable [] = {
|
||||
/* keyword keyword ID */
|
||||
{ "chapter", KEYWORD_chapter },
|
||||
{ "section", KEYWORD_section },
|
||||
{ "subsection", KEYWORD_subsection },
|
||||
{ "subsubsection", KEYWORD_subsubsection },
|
||||
{ "part", KEYWORD_part },
|
||||
{ "paragraph", KEYWORD_paragraph },
|
||||
{ "subparagraph", KEYWORD_subparagraph },
|
||||
{ "include", KEYWORD_include }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static boolean isIdentChar (const int c)
|
||||
{
|
||||
return (boolean)
|
||||
(isalpha (c) || isdigit (c) || c == '$' ||
|
||||
c == '_' || c == '#' || c == '-' || c == '.');
|
||||
}
|
||||
|
||||
static void buildTexKeywordHash (void)
|
||||
{
|
||||
const size_t count = sizeof (TexKeywordTable) /
|
||||
sizeof (TexKeywordTable [0]);
|
||||
size_t i;
|
||||
for (i = 0 ; i < count ; ++i)
|
||||
{
|
||||
const keywordDesc* const p = &TexKeywordTable [i];
|
||||
addKeyword (p->name, Lang_js, (int) p->id);
|
||||
}
|
||||
}
|
||||
|
||||
static tokenInfo *newToken (void)
|
||||
{
|
||||
tokenInfo *const token = xMalloc (1, tokenInfo);
|
||||
|
||||
token->type = TOKEN_UNDEFINED;
|
||||
token->keyword = KEYWORD_NONE;
|
||||
token->string = vStringNew ();
|
||||
token->scope = vStringNew ();
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static void deleteToken (tokenInfo *const token)
|
||||
{
|
||||
vStringDelete (token->string);
|
||||
vStringDelete (token->scope);
|
||||
eFree (token);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tag generation functions
|
||||
*/
|
||||
|
||||
static void makeConstTag (tokenInfo *const token, const texKind kind)
|
||||
{
|
||||
if (TexKinds [kind].enabled )
|
||||
{
|
||||
const char *const name = vStringValue (token->string);
|
||||
tagEntryInfo e;
|
||||
initTagEntry (&e, name);
|
||||
|
||||
e.lineNumber = token->lineNumber;
|
||||
e.filePosition = token->filePosition;
|
||||
e.kindName = TexKinds [kind].name;
|
||||
e.kind = TexKinds [kind].letter;
|
||||
|
||||
makeTagEntry (&e);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeTexTag (tokenInfo *const token, texKind kind)
|
||||
{
|
||||
vString * fulltag;
|
||||
|
||||
if (TexKinds [kind].enabled)
|
||||
{
|
||||
/*
|
||||
* If a scope has been added to the token, change the token
|
||||
* string to include the scope when making the tag.
|
||||
*/
|
||||
if ( vStringLength (token->scope) > 0 )
|
||||
{
|
||||
fulltag = vStringNew ();
|
||||
vStringCopy (fulltag, token->scope);
|
||||
vStringCatS (fulltag, ".");
|
||||
vStringCatS (fulltag, vStringValue (token->string));
|
||||
vStringTerminate (fulltag);
|
||||
vStringCopy (token->string, fulltag);
|
||||
vStringDelete (fulltag);
|
||||
}
|
||||
makeConstTag (token, kind);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parsing functions
|
||||
*/
|
||||
|
||||
static void parseString (vString *const string, const int delimiter)
|
||||
{
|
||||
boolean end = FALSE;
|
||||
while (! end)
|
||||
{
|
||||
int c = fileGetc ();
|
||||
if (c == EOF)
|
||||
end = TRUE;
|
||||
else if (c == '\\')
|
||||
{
|
||||
c = fileGetc(); /* This maybe a ' or ". */
|
||||
vStringPut (string, c);
|
||||
}
|
||||
else if (c == delimiter)
|
||||
end = TRUE;
|
||||
else
|
||||
vStringPut (string, c);
|
||||
}
|
||||
vStringTerminate (string);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a C identifier beginning with "firstChar" and places it into
|
||||
* "name".
|
||||
*/
|
||||
static void parseIdentifier (vString *const string, const int firstChar)
|
||||
{
|
||||
int c = firstChar;
|
||||
Assert (isIdentChar (c));
|
||||
do
|
||||
{
|
||||
vStringPut (string, c);
|
||||
c = fileGetc ();
|
||||
} while (isIdentChar (c));
|
||||
|
||||
vStringTerminate (string);
|
||||
if (!isspace (c))
|
||||
fileUngetc (c); /* unget non-identifier character */
|
||||
}
|
||||
|
||||
static void readToken (tokenInfo *const token)
|
||||
{
|
||||
int c;
|
||||
|
||||
token->type = TOKEN_UNDEFINED;
|
||||
token->keyword = KEYWORD_NONE;
|
||||
vStringClear (token->string);
|
||||
|
||||
getNextChar:
|
||||
do
|
||||
{
|
||||
c = fileGetc ();
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
}
|
||||
while (c == '\t' || c == ' ' || c == '\n');
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case EOF: longjmp (Exception, (int)ExceptionEOF); break;
|
||||
case '(': token->type = TOKEN_OPEN_PAREN; break;
|
||||
case ')': token->type = TOKEN_CLOSE_PAREN; break;
|
||||
case ',': token->type = TOKEN_COMMA; break;
|
||||
case '{': token->type = TOKEN_OPEN_CURLY; break;
|
||||
case '}': token->type = TOKEN_CLOSE_CURLY; break;
|
||||
case '[': token->type = TOKEN_OPEN_SQUARE; break;
|
||||
case ']': token->type = TOKEN_CLOSE_SQUARE; break;
|
||||
case '*': token->type = TOKEN_STAR; break;
|
||||
|
||||
case '\'':
|
||||
case '"':
|
||||
token->type = TOKEN_STRING;
|
||||
parseString (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
/*
|
||||
* All Tex tags start with a backslash.
|
||||
* Check if the next character is an alpha character
|
||||
* else it is not a potential tex tag.
|
||||
*/
|
||||
c = fileGetc ();
|
||||
if (! isalpha (c))
|
||||
fileUngetc (c);
|
||||
else
|
||||
{
|
||||
parseIdentifier (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
token->keyword = analyzeToken (token->string, Lang_js);
|
||||
if (isKeyword (token, KEYWORD_NONE))
|
||||
token->type = TOKEN_IDENTIFIER;
|
||||
else
|
||||
token->type = TOKEN_KEYWORD;
|
||||
}
|
||||
break;
|
||||
|
||||
case '%':
|
||||
fileSkipToCharacter ('\n'); /* % are single line comments */
|
||||
goto getNextChar;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (! isIdentChar (c))
|
||||
token->type = TOKEN_UNDEFINED;
|
||||
else
|
||||
{
|
||||
parseIdentifier (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
token->type = TOKEN_IDENTIFIER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void copyToken (tokenInfo *const dest, tokenInfo *const src)
|
||||
{
|
||||
dest->lineNumber = src->lineNumber;
|
||||
dest->filePosition = src->filePosition;
|
||||
dest->type = src->type;
|
||||
dest->keyword = src->keyword;
|
||||
vStringCopy (dest->string, src->string);
|
||||
vStringCopy (dest->scope, src->scope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scanning functions
|
||||
*/
|
||||
|
||||
static boolean parseTag (tokenInfo *const token, texKind kind)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
vString * fullname;
|
||||
boolean useLongName = TRUE;
|
||||
|
||||
fullname = vStringNew ();
|
||||
vStringClear (fullname);
|
||||
|
||||
/*
|
||||
* Tex tags are of these formats:
|
||||
* \keyword{any number of words}
|
||||
* \keyword[short desc]{any number of words}
|
||||
* \keyword*[short desc]{any number of words}
|
||||
*
|
||||
* When a keyword is found, loop through all words within
|
||||
* the curly braces for the tag name.
|
||||
*/
|
||||
|
||||
if (isType (token, TOKEN_KEYWORD))
|
||||
{
|
||||
copyToken (name, token);
|
||||
readToken (token);
|
||||
}
|
||||
|
||||
if (isType (token, TOKEN_OPEN_SQUARE))
|
||||
{
|
||||
useLongName = FALSE;
|
||||
|
||||
readToken (token);
|
||||
while (! isType (token, TOKEN_CLOSE_SQUARE) )
|
||||
{
|
||||
if (isType (token, TOKEN_IDENTIFIER))
|
||||
{
|
||||
if (fullname->length > 0)
|
||||
vStringCatS (fullname, " ");
|
||||
vStringCatS (fullname, vStringValue (token->string));
|
||||
}
|
||||
readToken (token);
|
||||
}
|
||||
vStringTerminate (fullname);
|
||||
vStringCopy (name->string, fullname);
|
||||
makeTexTag (name, kind);
|
||||
}
|
||||
|
||||
if (isType (token, TOKEN_STAR))
|
||||
{
|
||||
readToken (token);
|
||||
}
|
||||
|
||||
if (isType (token, TOKEN_OPEN_CURLY))
|
||||
{
|
||||
readToken (token);
|
||||
while (! isType (token, TOKEN_CLOSE_CURLY) )
|
||||
{
|
||||
/* if (isType (token, TOKEN_IDENTIFIER) && useLongName) */
|
||||
if (useLongName)
|
||||
{
|
||||
if (fullname->length > 0)
|
||||
vStringCatS (fullname, " ");
|
||||
vStringCatS (fullname, vStringValue (token->string));
|
||||
}
|
||||
readToken (token);
|
||||
}
|
||||
if (useLongName)
|
||||
{
|
||||
vStringTerminate (fullname);
|
||||
vStringCopy (name->string, fullname);
|
||||
makeTexTag (name, kind);
|
||||
}
|
||||
}
|
||||
|
||||
deleteToken (name);
|
||||
vStringDelete (fullname);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void parseTexFile (tokenInfo *const token)
|
||||
{
|
||||
do
|
||||
{
|
||||
readToken (token);
|
||||
|
||||
if (isType (token, TOKEN_KEYWORD))
|
||||
{
|
||||
switch (token->keyword)
|
||||
{
|
||||
case KEYWORD_chapter:
|
||||
parseTag (token, TEXTAG_CHAPTER);
|
||||
break;
|
||||
case KEYWORD_section:
|
||||
parseTag (token, TEXTAG_SECTION);
|
||||
break;
|
||||
case KEYWORD_subsection:
|
||||
parseTag (token, TEXTAG_SUBSECTION);
|
||||
break;
|
||||
case KEYWORD_subsubsection:
|
||||
parseTag (token, TEXTAG_SUBSUBSECTION);
|
||||
break;
|
||||
case KEYWORD_part:
|
||||
parseTag (token, TEXTAG_PART);
|
||||
break;
|
||||
case KEYWORD_paragraph:
|
||||
parseTag (token, TEXTAG_PARAGRAPH);
|
||||
break;
|
||||
case KEYWORD_subparagraph:
|
||||
parseTag (token, TEXTAG_SUBPARAGRAPH);
|
||||
break;
|
||||
case KEYWORD_include:
|
||||
parseTag (token, TEXTAG_INCLUDE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (TRUE);
|
||||
}
|
||||
|
||||
static void initialize (const langType language)
|
||||
{
|
||||
Assert (sizeof (TexKinds) / sizeof (TexKinds [0]) == TEXTAG_COUNT);
|
||||
Lang_js = language;
|
||||
buildTexKeywordHash ();
|
||||
}
|
||||
|
||||
static void findTexTags (void)
|
||||
{
|
||||
tokenInfo *const token = newToken ();
|
||||
exception_t exception;
|
||||
|
||||
exception = (exception_t) (setjmp (Exception));
|
||||
while (exception == ExceptionNone)
|
||||
parseTexFile (token);
|
||||
|
||||
deleteToken (token);
|
||||
}
|
||||
|
||||
/* Create parser definition stucture */
|
||||
extern parserDefinition* TexParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "tex", NULL };
|
||||
parserDefinition *const def = parserNew ("Tex");
|
||||
def->extensions = extensions;
|
||||
/*
|
||||
* New definitions for parsing instead of regex
|
||||
*/
|
||||
def->kinds = TexKinds;
|
||||
def->kindCount = KIND_COUNT (TexKinds);
|
||||
def->parser = findTexTags;
|
||||
def->initialize = initialize;
|
||||
|
||||
return def;
|
||||
}
|
||||
/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
|
343
third_party/ctags/verilog.c
vendored
Normal file
343
third_party/ctags/verilog.c
vendored
Normal file
|
@ -0,0 +1,343 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: verilog.c 753 2010-02-27 17:53:32Z elliotth $
|
||||
*
|
||||
* 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 the Verilog HDL
|
||||
* (Hardware Description Language).
|
||||
*
|
||||
* Language definition documents:
|
||||
* http://www.eg.bucknell.edu/~cs320/verilog/verilog-manual.html
|
||||
* http://www.sutherland-hdl.com/on-line_ref_guide/vlog_ref_top.html
|
||||
* http://www.verilog.com/VerilogBNF.html
|
||||
* http://eesun.free.fr/DOC/VERILOG/verilog_manual1.html
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/get.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
||||
|
||||
typedef enum {
|
||||
K_UNDEFINED = -1,
|
||||
K_CONSTANT,
|
||||
K_EVENT,
|
||||
K_FUNCTION,
|
||||
K_MODULE,
|
||||
K_NET,
|
||||
K_PORT,
|
||||
K_REGISTER,
|
||||
K_TASK
|
||||
} verilogKind;
|
||||
|
||||
typedef struct {
|
||||
const char *keyword;
|
||||
verilogKind kind;
|
||||
} keywordAssoc;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static int Ungetc;
|
||||
static int Lang_verilog;
|
||||
static jmp_buf Exception;
|
||||
|
||||
static kindOption VerilogKinds [] = {
|
||||
{ TRUE, 'c', "constant", "constants (define, parameter, specparam)" },
|
||||
{ TRUE, 'e', "event", "events" },
|
||||
{ TRUE, 'f', "function", "functions" },
|
||||
{ TRUE, 'm', "module", "modules" },
|
||||
{ TRUE, 'n', "net", "net data types" },
|
||||
{ TRUE, 'p', "port", "ports" },
|
||||
{ TRUE, 'r', "register", "register data types" },
|
||||
{ TRUE, 't', "task", "tasks" }
|
||||
};
|
||||
|
||||
static keywordAssoc VerilogKeywordTable [] = {
|
||||
{ "`define", K_CONSTANT },
|
||||
{ "event", K_EVENT },
|
||||
{ "function", K_FUNCTION },
|
||||
{ "inout", K_PORT },
|
||||
{ "input", K_PORT },
|
||||
{ "integer", K_REGISTER },
|
||||
{ "module", K_MODULE },
|
||||
{ "output", K_PORT },
|
||||
{ "parameter", K_CONSTANT },
|
||||
{ "real", K_REGISTER },
|
||||
{ "realtime", K_REGISTER },
|
||||
{ "reg", K_REGISTER },
|
||||
{ "specparam", K_CONSTANT },
|
||||
{ "supply0", K_NET },
|
||||
{ "supply1", K_NET },
|
||||
{ "task", K_TASK },
|
||||
{ "time", K_REGISTER },
|
||||
{ "tri0", K_NET },
|
||||
{ "tri1", K_NET },
|
||||
{ "triand", K_NET },
|
||||
{ "tri", K_NET },
|
||||
{ "trior", K_NET },
|
||||
{ "trireg", K_NET },
|
||||
{ "wand", K_NET },
|
||||
{ "wire", K_NET },
|
||||
{ "wor", K_NET }
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void initialize (const langType language)
|
||||
{
|
||||
size_t i;
|
||||
const size_t count =
|
||||
sizeof (VerilogKeywordTable) / sizeof (VerilogKeywordTable [0]);
|
||||
Lang_verilog = language;
|
||||
for (i = 0 ; i < count ; ++i)
|
||||
{
|
||||
const keywordAssoc* const p = &VerilogKeywordTable [i];
|
||||
addKeyword (p->keyword, language, (int) p->kind);
|
||||
}
|
||||
}
|
||||
|
||||
static void vUngetc (int c)
|
||||
{
|
||||
Assert (Ungetc == '\0');
|
||||
Ungetc = c;
|
||||
}
|
||||
|
||||
static int vGetc (void)
|
||||
{
|
||||
int c;
|
||||
if (Ungetc == '\0')
|
||||
c = fileGetc ();
|
||||
else
|
||||
{
|
||||
c = Ungetc;
|
||||
Ungetc = '\0';
|
||||
}
|
||||
if (c == '/')
|
||||
{
|
||||
int c2 = fileGetc ();
|
||||
if (c2 == EOF)
|
||||
longjmp (Exception, (int) ExceptionEOF);
|
||||
else if (c2 == '/') /* strip comment until end-of-line */
|
||||
{
|
||||
do
|
||||
c = fileGetc ();
|
||||
while (c != '\n' && c != EOF);
|
||||
}
|
||||
else if (c2 == '*') /* strip block comment */
|
||||
{
|
||||
c = skipOverCComment();
|
||||
}
|
||||
else
|
||||
{
|
||||
fileUngetc (c2);
|
||||
}
|
||||
}
|
||||
else if (c == '"') /* strip string contents */
|
||||
{
|
||||
int c2;
|
||||
do
|
||||
c2 = fileGetc ();
|
||||
while (c2 != '"' && c2 != EOF);
|
||||
c = '@';
|
||||
}
|
||||
if (c == EOF)
|
||||
longjmp (Exception, (int) ExceptionEOF);
|
||||
return c;
|
||||
}
|
||||
|
||||
static boolean isIdentifierCharacter (const int c)
|
||||
{
|
||||
return (boolean)(isalnum (c) || c == '_' || c == '`');
|
||||
}
|
||||
|
||||
static int skipWhite (int c)
|
||||
{
|
||||
while (isspace (c))
|
||||
c = vGetc ();
|
||||
return c;
|
||||
}
|
||||
|
||||
static int skipPastMatch (const char *const pair)
|
||||
{
|
||||
const int begin = pair [0], end = pair [1];
|
||||
int matchLevel = 1;
|
||||
int c;
|
||||
do
|
||||
{
|
||||
c = vGetc ();
|
||||
if (c == begin)
|
||||
++matchLevel;
|
||||
else if (c == end)
|
||||
--matchLevel;
|
||||
}
|
||||
while (matchLevel > 0);
|
||||
return vGetc ();
|
||||
}
|
||||
|
||||
static boolean readIdentifier (vString *const name, int c)
|
||||
{
|
||||
vStringClear (name);
|
||||
if (isIdentifierCharacter (c))
|
||||
{
|
||||
while (isIdentifierCharacter (c))
|
||||
{
|
||||
vStringPut (name, c);
|
||||
c = vGetc ();
|
||||
}
|
||||
vUngetc (c);
|
||||
vStringTerminate (name);
|
||||
}
|
||||
return (boolean)(name->length > 0);
|
||||
}
|
||||
|
||||
static void tagNameList (const verilogKind kind, int c)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
boolean repeat;
|
||||
Assert (isIdentifierCharacter (c));
|
||||
do
|
||||
{
|
||||
repeat = FALSE;
|
||||
if (isIdentifierCharacter (c))
|
||||
{
|
||||
readIdentifier (name, c);
|
||||
makeSimpleTag (name, VerilogKinds, kind);
|
||||
}
|
||||
else
|
||||
break;
|
||||
c = skipWhite (vGetc ());
|
||||
if (c == '[')
|
||||
c = skipPastMatch ("[]");
|
||||
c = skipWhite (c);
|
||||
if (c == '=')
|
||||
{
|
||||
c = skipWhite (vGetc ());
|
||||
if (c == '{')
|
||||
skipPastMatch ("{}");
|
||||
else
|
||||
{
|
||||
do
|
||||
c = vGetc ();
|
||||
while (c != ',' && c != ';');
|
||||
}
|
||||
}
|
||||
if (c == ',')
|
||||
{
|
||||
c = skipWhite (vGetc ());
|
||||
repeat = TRUE;
|
||||
}
|
||||
else
|
||||
repeat = FALSE;
|
||||
} while (repeat);
|
||||
vStringDelete (name);
|
||||
vUngetc (c);
|
||||
}
|
||||
|
||||
static void findTag (vString *const name)
|
||||
{
|
||||
const verilogKind kind = (verilogKind) lookupKeyword (vStringValue (name), Lang_verilog);
|
||||
if (kind == K_CONSTANT && vStringItem (name, 0) == '`')
|
||||
{
|
||||
/* Bug #961001: Verilog compiler directives are line-based. */
|
||||
int c = skipWhite (vGetc ());
|
||||
readIdentifier (name, c);
|
||||
makeSimpleTag (name, VerilogKinds, kind);
|
||||
/* Skip the rest of the line. */
|
||||
do {
|
||||
c = vGetc();
|
||||
} while (c != '\n');
|
||||
vUngetc (c);
|
||||
}
|
||||
else if (kind != K_UNDEFINED)
|
||||
{
|
||||
int c = skipWhite (vGetc ());
|
||||
|
||||
/* Many keywords can have bit width.
|
||||
* reg [3:0] net_name;
|
||||
* inout [(`DBUSWIDTH-1):0] databus;
|
||||
*/
|
||||
if (c == '(')
|
||||
c = skipPastMatch ("()");
|
||||
c = skipWhite (c);
|
||||
if (c == '[')
|
||||
c = skipPastMatch ("[]");
|
||||
c = skipWhite (c);
|
||||
if (c == '#')
|
||||
{
|
||||
c = vGetc ();
|
||||
if (c == '(')
|
||||
c = skipPastMatch ("()");
|
||||
}
|
||||
c = skipWhite (c);
|
||||
if (isIdentifierCharacter (c))
|
||||
tagNameList (kind, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void findVerilogTags (void)
|
||||
{
|
||||
vString *const name = vStringNew ();
|
||||
volatile boolean newStatement = TRUE;
|
||||
volatile int c = '\0';
|
||||
exception_t exception = (exception_t) setjmp (Exception);
|
||||
|
||||
if (exception == ExceptionNone) while (c != EOF)
|
||||
{
|
||||
c = vGetc ();
|
||||
switch (c)
|
||||
{
|
||||
case ';':
|
||||
case '\n':
|
||||
newStatement = TRUE;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
|
||||
default:
|
||||
if (newStatement && readIdentifier (name, c))
|
||||
findTag (name);
|
||||
newStatement = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
extern parserDefinition* VerilogParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "v", NULL };
|
||||
parserDefinition* def = parserNew ("Verilog");
|
||||
def->kinds = VerilogKinds;
|
||||
def->kindCount = KIND_COUNT (VerilogKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findVerilogTags;
|
||||
def->initialize = initialize;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
837
third_party/ctags/vhdl.c
vendored
Normal file
837
third_party/ctags/vhdl.c
vendored
Normal file
|
@ -0,0 +1,837 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: vhdl.c 652 2008-04-18 03:51:47Z elliotth $
|
||||
*
|
||||
* Copyright (c) 2008, Nicolas Vincent
|
||||
*
|
||||
* 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 VHDL files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/str/str.h" /* to define isalpha () */
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/entry.h"
|
||||
#include "third_party/ctags/keyword.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#define isType(token,t) (boolean) ((token)->type == (t))
|
||||
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
||||
|
||||
/*
|
||||
* Used to specify type of keyword.
|
||||
*/
|
||||
typedef enum eKeywordId {
|
||||
KEYWORD_NONE = -1,
|
||||
KEYWORD_ABS,
|
||||
KEYWORD_ACCESS,
|
||||
KEYWORD_AFTER,
|
||||
KEYWORD_ALIAS,
|
||||
KEYWORD_ALL,
|
||||
KEYWORD_AND,
|
||||
KEYWORD_ARCHITECTURE,
|
||||
KEYWORD_ARRAY,
|
||||
KEYWORD_ASSERT,
|
||||
KEYWORD_ATTRIBUTE,
|
||||
KEYWORD_BEGIN,
|
||||
KEYWORD_BLOCK,
|
||||
KEYWORD_BODY,
|
||||
KEYWORD_BUFFER,
|
||||
KEYWORD_BUS,
|
||||
KEYWORD_CASE,
|
||||
KEYWORD_COMPONENT,
|
||||
KEYWORD_CONFIGURATION,
|
||||
KEYWORD_CONSTANT,
|
||||
KEYWORD_DISCONNECT,
|
||||
KEYWORD_DOWNTO,
|
||||
KEYWORD_ELSE,
|
||||
KEYWORD_ELSIF,
|
||||
KEYWORD_END,
|
||||
KEYWORD_ENTITY,
|
||||
KEYWORD_EXIT,
|
||||
KEYWORD_FILE,
|
||||
KEYWORD_FOR,
|
||||
KEYWORD_FUNCTION,
|
||||
KEYWORD_GENERATE,
|
||||
KEYWORD_GENERIC,
|
||||
KEYWORD_GROUP,
|
||||
KEYWORD_GUARDED,
|
||||
KEYWORD_IF,
|
||||
KEYWORD_IMPURE,
|
||||
KEYWORD_IN,
|
||||
KEYWORD_INERTIAL,
|
||||
KEYWORD_INOUT,
|
||||
KEYWORD_IS,
|
||||
KEYWORD_LABEL,
|
||||
KEYWORD_LIBRARY,
|
||||
KEYWORD_LINKAGE,
|
||||
KEYWORD_LITERAL,
|
||||
KEYWORD_LOOP,
|
||||
KEYWORD_MAP,
|
||||
KEYWORD_MOD,
|
||||
KEYWORD_NAND,
|
||||
KEYWORD_NEW,
|
||||
KEYWORD_NEXT,
|
||||
KEYWORD_NOR,
|
||||
KEYWORD_NOT,
|
||||
KEYWORD_NULL,
|
||||
KEYWORD_OF,
|
||||
KEYWORD_ON,
|
||||
KEYWORD_OPEN,
|
||||
KEYWORD_OR,
|
||||
KEYWORD_OTHERS,
|
||||
KEYWORD_OUT,
|
||||
KEYWORD_PACKAGE,
|
||||
KEYWORD_PORT,
|
||||
KEYWORD_POSTPONED,
|
||||
KEYWORD_PROCEDURE,
|
||||
KEYWORD_PROCESS,
|
||||
KEYWORD_PURE,
|
||||
KEYWORD_RANGE,
|
||||
KEYWORD_RECORD,
|
||||
KEYWORD_REGISTER,
|
||||
KEYWORD_REJECT,
|
||||
KEYWORD_RETURN,
|
||||
KEYWORD_ROL,
|
||||
KEYWORD_ROR,
|
||||
KEYWORD_SELECT,
|
||||
KEYWORD_SEVERITY,
|
||||
KEYWORD_SIGNAL,
|
||||
KEYWORD_SHARED,
|
||||
KEYWORD_SLA,
|
||||
KEYWORD_SLI,
|
||||
KEYWORD_SRA,
|
||||
KEYWORD_SRL,
|
||||
KEYWORD_SUBTYPE,
|
||||
KEYWORD_THEN,
|
||||
KEYWORD_TO,
|
||||
KEYWORD_TRANSPORT,
|
||||
KEYWORD_TYPE,
|
||||
KEYWORD_UNAFFECTED,
|
||||
KEYWORD_UNITS,
|
||||
KEYWORD_UNTIL,
|
||||
KEYWORD_USE,
|
||||
KEYWORD_VARIABLE,
|
||||
KEYWORD_WAIT,
|
||||
KEYWORD_WHEN,
|
||||
KEYWORD_WHILE,
|
||||
KEYWORD_WITH,
|
||||
KEYWORD_XNOR,
|
||||
KEYWORD_XOR
|
||||
} keywordId;
|
||||
|
||||
/* Used to determine whether keyword is valid for the current language and
|
||||
* what its ID is.
|
||||
*/
|
||||
typedef struct sKeywordDesc {
|
||||
const char *name;
|
||||
keywordId id;
|
||||
} keywordDesc;
|
||||
|
||||
typedef enum eTokenType {
|
||||
TOKEN_NONE, /* none */
|
||||
TOKEN_OPEN_PAREN, /* ( */
|
||||
TOKEN_CLOSE_PAREN, /* ) */
|
||||
TOKEN_COMMA, /* the comma character */
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_KEYWORD,
|
||||
TOKEN_PERIOD, /* . */
|
||||
TOKEN_OPERATOR,
|
||||
TOKEN_SEMICOLON, /* the semicolon character */
|
||||
TOKEN_STRING
|
||||
} tokenType;
|
||||
|
||||
typedef struct sTokenInfo {
|
||||
tokenType type;
|
||||
keywordId keyword;
|
||||
vString *string; /* the name of the token */
|
||||
vString *scope;
|
||||
unsigned long lineNumber; /* line number of tag */
|
||||
fpos_t filePosition; /* file position of line containing name */
|
||||
} tokenInfo;
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static int Lang_vhdl;
|
||||
static jmp_buf Exception;
|
||||
|
||||
/* Used to index into the VhdlKinds table. */
|
||||
typedef enum {
|
||||
VHDLTAG_UNDEFINED = -1,
|
||||
VHDLTAG_CONSTANT,
|
||||
VHDLTAG_TYPE,
|
||||
VHDLTAG_SUBTYPE,
|
||||
VHDLTAG_RECORD,
|
||||
VHDLTAG_ENTITY,
|
||||
VHDLTAG_COMPONENT,
|
||||
VHDLTAG_PROTOTYPE,
|
||||
VHDLTAG_FUNCTION,
|
||||
VHDLTAG_PROCEDURE,
|
||||
VHDLTAG_PACKAGE,
|
||||
VHDLTAG_LOCAL
|
||||
} vhdlKind;
|
||||
|
||||
static kindOption VhdlKinds[] = {
|
||||
{TRUE, 'c', "constant", "constant declarations"},
|
||||
{TRUE, 't', "type", "type definitions"},
|
||||
{TRUE, 'T', "subtype", "subtype definitions"},
|
||||
{TRUE, 'r', "record", "record names"},
|
||||
{TRUE, 'e', "entity", "entity declarations"},
|
||||
{FALSE, 'C', "component", "component declarations"},
|
||||
{FALSE, 'd', "prototype", "prototypes"},
|
||||
{TRUE, 'f', "function", "function prototypes and declarations"},
|
||||
{TRUE, 'p', "procedure", "procedure prototypes and declarations"},
|
||||
{TRUE, 'P', "package", "package definitions"},
|
||||
{FALSE, 'l', "local", "local definitions"}
|
||||
};
|
||||
|
||||
static keywordDesc VhdlKeywordTable[] = {
|
||||
{"abs", KEYWORD_ABS},
|
||||
{"access", KEYWORD_ACCESS},
|
||||
{"after", KEYWORD_AFTER},
|
||||
{"alias", KEYWORD_ALIAS},
|
||||
{"all", KEYWORD_ALL},
|
||||
{"and", KEYWORD_AND},
|
||||
{"architecture", KEYWORD_ARCHITECTURE},
|
||||
{"array", KEYWORD_ARRAY},
|
||||
{"assert", KEYWORD_ASSERT},
|
||||
{"attribute", KEYWORD_ATTRIBUTE},
|
||||
{"begin", KEYWORD_BEGIN},
|
||||
{"block", KEYWORD_BLOCK},
|
||||
{"body", KEYWORD_BODY},
|
||||
{"buffer", KEYWORD_BUFFER},
|
||||
{"bus", KEYWORD_BUS},
|
||||
{"case", KEYWORD_CASE},
|
||||
{"component", KEYWORD_COMPONENT},
|
||||
{"configuration", KEYWORD_CONFIGURATION},
|
||||
{"constant", KEYWORD_CONSTANT},
|
||||
{"disconnect", KEYWORD_DISCONNECT},
|
||||
{"downto", KEYWORD_DOWNTO},
|
||||
{"else", KEYWORD_ELSE},
|
||||
{"elsif", KEYWORD_ELSIF},
|
||||
{"end", KEYWORD_END},
|
||||
{"entity", KEYWORD_ENTITY},
|
||||
{"exit", KEYWORD_EXIT},
|
||||
{"file", KEYWORD_FILE},
|
||||
{"for", KEYWORD_FOR},
|
||||
{"function", KEYWORD_FUNCTION},
|
||||
{"generate", KEYWORD_GENERATE},
|
||||
{"generic", KEYWORD_GENERIC},
|
||||
{"group", KEYWORD_GROUP},
|
||||
{"guarded", KEYWORD_GUARDED},
|
||||
{"if", KEYWORD_IF},
|
||||
{"impure", KEYWORD_IMPURE},
|
||||
{"in", KEYWORD_IN},
|
||||
{"inertial", KEYWORD_INERTIAL},
|
||||
{"inout", KEYWORD_INOUT},
|
||||
{"is", KEYWORD_IS},
|
||||
{"label", KEYWORD_LABEL},
|
||||
{"library", KEYWORD_LIBRARY},
|
||||
{"linkage", KEYWORD_LINKAGE},
|
||||
{"literal", KEYWORD_LITERAL},
|
||||
{"loop", KEYWORD_LOOP},
|
||||
{"map", KEYWORD_MAP},
|
||||
{"mod", KEYWORD_MOD},
|
||||
{"nand", KEYWORD_NAND},
|
||||
{"new", KEYWORD_NEW},
|
||||
{"next", KEYWORD_NEXT},
|
||||
{"nor", KEYWORD_NOR},
|
||||
{"not", KEYWORD_NOT},
|
||||
{"null", KEYWORD_NULL},
|
||||
{"of", KEYWORD_OF},
|
||||
{"on", KEYWORD_ON},
|
||||
{"open", KEYWORD_OPEN},
|
||||
{"or", KEYWORD_OR},
|
||||
{"others", KEYWORD_OTHERS},
|
||||
{"out", KEYWORD_OUT},
|
||||
{"package", KEYWORD_PACKAGE},
|
||||
{"port", KEYWORD_PORT},
|
||||
{"postponed", KEYWORD_POSTPONED},
|
||||
{"procedure", KEYWORD_PROCEDURE},
|
||||
{"process", KEYWORD_PROCESS},
|
||||
{"pure", KEYWORD_PURE},
|
||||
{"range", KEYWORD_RANGE},
|
||||
{"record", KEYWORD_RECORD},
|
||||
{"register", KEYWORD_REGISTER},
|
||||
{"reject", KEYWORD_REJECT},
|
||||
{"return", KEYWORD_RETURN},
|
||||
{"rol", KEYWORD_ROL},
|
||||
{"ror", KEYWORD_ROR},
|
||||
{"select", KEYWORD_SELECT},
|
||||
{"severity", KEYWORD_SEVERITY},
|
||||
{"signal", KEYWORD_SIGNAL},
|
||||
{"shared", KEYWORD_SHARED},
|
||||
{"sla", KEYWORD_SLA},
|
||||
{"sli", KEYWORD_SLI},
|
||||
{"sra", KEYWORD_SRA},
|
||||
{"srl", KEYWORD_SRL},
|
||||
{"subtype", KEYWORD_SUBTYPE},
|
||||
{"then", KEYWORD_THEN},
|
||||
{"to", KEYWORD_TO},
|
||||
{"transport", KEYWORD_TRANSPORT},
|
||||
{"type", KEYWORD_TYPE},
|
||||
{"unaffected", KEYWORD_UNAFFECTED},
|
||||
{"units", KEYWORD_UNITS},
|
||||
{"until", KEYWORD_UNTIL},
|
||||
{"use", KEYWORD_USE},
|
||||
{"variable", KEYWORD_VARIABLE},
|
||||
{"wait", KEYWORD_WAIT},
|
||||
{"when", KEYWORD_WHEN},
|
||||
{"while", KEYWORD_WHILE},
|
||||
{"with", KEYWORD_WITH},
|
||||
{"xnor", KEYWORD_XNOR},
|
||||
{"xor", KEYWORD_XOR}
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION DECLARATIONS
|
||||
*/
|
||||
static void parseKeywords (tokenInfo * const token, boolean local);
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static boolean isIdentChar1 (const int c)
|
||||
{
|
||||
return (boolean) (isalpha (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isIdentChar (const int c)
|
||||
{
|
||||
return (boolean) (isalpha (c) || isdigit (c) || c == '_');
|
||||
}
|
||||
|
||||
static boolean isIdentifierMatch (const tokenInfo * const token,
|
||||
const vString * const name)
|
||||
{
|
||||
return (boolean) (isType (token, TOKEN_IDENTIFIER) &&
|
||||
strcasecmp (vStringValue (token->string), vStringValue (name)) == 0);
|
||||
/* XXX this is copy/paste from eiffel.c and slightly modified */
|
||||
/* shouldn't we use strNcasecmp ? */
|
||||
}
|
||||
|
||||
static boolean isKeywordOrIdent (const tokenInfo * const token,
|
||||
const keywordId keyword, const vString * const name)
|
||||
{
|
||||
return (boolean) (isKeyword (token, keyword) ||
|
||||
isIdentifierMatch (token, name));
|
||||
}
|
||||
|
||||
static tokenInfo *newToken (void)
|
||||
{
|
||||
tokenInfo *const token = xMalloc (1, tokenInfo);
|
||||
token->type = TOKEN_NONE;
|
||||
token->keyword = KEYWORD_NONE;
|
||||
token->string = vStringNew ();
|
||||
token->scope = vStringNew ();
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
return token;
|
||||
}
|
||||
|
||||
static void deleteToken (tokenInfo * const token)
|
||||
{
|
||||
if (token != NULL)
|
||||
{
|
||||
vStringDelete (token->string);
|
||||
vStringDelete (token->scope);
|
||||
eFree (token);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parsing functions
|
||||
*/
|
||||
|
||||
static void parseString (vString * const string, const int delimiter)
|
||||
{
|
||||
boolean end = FALSE;
|
||||
while (!end)
|
||||
{
|
||||
int c = fileGetc ();
|
||||
if (c == EOF)
|
||||
end = TRUE;
|
||||
else if (c == '\\')
|
||||
{
|
||||
c = fileGetc (); /* This maybe a ' or ". */
|
||||
vStringPut (string, c);
|
||||
}
|
||||
else if (c == delimiter)
|
||||
end = TRUE;
|
||||
else
|
||||
vStringPut (string, c);
|
||||
}
|
||||
vStringTerminate (string);
|
||||
}
|
||||
|
||||
/* Read a VHDL identifier beginning with "firstChar" and place it into "name".
|
||||
*/
|
||||
static void parseIdentifier (vString * const string, const int firstChar)
|
||||
{
|
||||
int c = firstChar;
|
||||
Assert (isIdentChar1 (c));
|
||||
do
|
||||
{
|
||||
vStringPut (string, c);
|
||||
c = fileGetc ();
|
||||
} while (isIdentChar (c));
|
||||
vStringTerminate (string);
|
||||
if (!isspace (c))
|
||||
fileUngetc (c); /* unget non-identifier character */
|
||||
}
|
||||
|
||||
static void readToken (tokenInfo * const token)
|
||||
{
|
||||
int c;
|
||||
|
||||
token->type = TOKEN_NONE;
|
||||
token->keyword = KEYWORD_NONE;
|
||||
vStringClear (token->string);
|
||||
|
||||
getNextChar:
|
||||
do
|
||||
{
|
||||
c = fileGetc ();
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
}
|
||||
while (c == '\t' || c == ' ' || c == '\n');
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case EOF:
|
||||
longjmp (Exception, (int) ExceptionEOF);
|
||||
break;
|
||||
case '(':
|
||||
token->type = TOKEN_OPEN_PAREN;
|
||||
break;
|
||||
case ')':
|
||||
token->type = TOKEN_CLOSE_PAREN;
|
||||
break;
|
||||
case ';':
|
||||
token->type = TOKEN_SEMICOLON;
|
||||
break;
|
||||
case '.':
|
||||
token->type = TOKEN_PERIOD;
|
||||
break;
|
||||
case ',':
|
||||
token->type = TOKEN_COMMA;
|
||||
break;
|
||||
case '\'': /* only single char are inside simple quotes */
|
||||
break; /* or it is for attributes so we don't care */
|
||||
case '"':
|
||||
token->type = TOKEN_STRING;
|
||||
parseString (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
break;
|
||||
case '-':
|
||||
c = fileGetc ();
|
||||
if (c == '-') /* start of a comment */
|
||||
{
|
||||
fileSkipToCharacter ('\n');
|
||||
goto getNextChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isspace (c))
|
||||
fileUngetc (c);
|
||||
token->type = TOKEN_OPERATOR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!isIdentChar1 (c))
|
||||
token->type = TOKEN_NONE;
|
||||
else
|
||||
{
|
||||
parseIdentifier (token->string, c);
|
||||
token->lineNumber = getSourceLineNumber ();
|
||||
token->filePosition = getInputFilePosition ();
|
||||
token->keyword = analyzeToken (token->string, Lang_vhdl);
|
||||
if (isKeyword (token, KEYWORD_NONE))
|
||||
token->type = TOKEN_IDENTIFIER;
|
||||
else
|
||||
token->type = TOKEN_KEYWORD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void skipToKeyword (const keywordId keyword)
|
||||
{
|
||||
tokenInfo *const token = newToken ();
|
||||
do
|
||||
{
|
||||
readToken (token);
|
||||
}
|
||||
while (!isKeyword (token, keyword));
|
||||
deleteToken (token);
|
||||
}
|
||||
|
||||
static void skipToMatched (tokenInfo * const token)
|
||||
{
|
||||
int nest_level = 0;
|
||||
tokenType open_token;
|
||||
tokenType close_token;
|
||||
|
||||
switch (token->type)
|
||||
{
|
||||
case TOKEN_OPEN_PAREN:
|
||||
open_token = TOKEN_OPEN_PAREN;
|
||||
close_token = TOKEN_CLOSE_PAREN;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine will skip to a matching closing token.
|
||||
* It will also handle nested tokens like the (, ) below.
|
||||
* ( name varchar(30), text binary(10) )
|
||||
*/
|
||||
if (isType (token, open_token))
|
||||
{
|
||||
nest_level++;
|
||||
while (!(isType (token, close_token) && (nest_level == 0)))
|
||||
{
|
||||
readToken (token);
|
||||
if (isType (token, open_token))
|
||||
{
|
||||
nest_level++;
|
||||
}
|
||||
if (isType (token, close_token))
|
||||
{
|
||||
if (nest_level > 0)
|
||||
{
|
||||
nest_level--;
|
||||
}
|
||||
}
|
||||
}
|
||||
readToken (token);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeConstTag (tokenInfo * const token, const vhdlKind kind)
|
||||
{
|
||||
if (VhdlKinds[kind].enabled)
|
||||
{
|
||||
const char *const name = vStringValue (token->string);
|
||||
tagEntryInfo e;
|
||||
initTagEntry (&e, name);
|
||||
e.lineNumber = token->lineNumber;
|
||||
e.filePosition = token->filePosition;
|
||||
e.kindName = VhdlKinds[kind].name;
|
||||
e.kind = VhdlKinds[kind].letter;
|
||||
makeTagEntry (&e);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeVhdlTag (tokenInfo * const token, const vhdlKind kind)
|
||||
{
|
||||
if (VhdlKinds[kind].enabled)
|
||||
{
|
||||
/*
|
||||
* If a scope has been added to the token, change the token
|
||||
* string to include the scope when making the tag.
|
||||
*/
|
||||
if (vStringLength (token->scope) > 0)
|
||||
{
|
||||
vString *fulltag = vStringNew ();
|
||||
vStringCopy (fulltag, token->scope);
|
||||
vStringCatS (fulltag, ".");
|
||||
vStringCatS (fulltag, vStringValue (token->string));
|
||||
vStringTerminate (fulltag);
|
||||
vStringCopy (token->string, fulltag);
|
||||
vStringDelete (fulltag);
|
||||
}
|
||||
makeConstTag (token, kind);
|
||||
}
|
||||
}
|
||||
|
||||
static void initialize (const langType language)
|
||||
{
|
||||
size_t i;
|
||||
const size_t count =
|
||||
sizeof (VhdlKeywordTable) / sizeof (VhdlKeywordTable[0]);
|
||||
Lang_vhdl = language;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
const keywordDesc *const p = &VhdlKeywordTable[i];
|
||||
addKeyword (p->name, language, (int) p->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void parsePackage (tokenInfo * const token)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
Assert (isKeyword (token, KEYWORD_PACKAGE));
|
||||
readToken (token);
|
||||
if (isKeyword (token, KEYWORD_BODY))
|
||||
{
|
||||
readToken (name);
|
||||
makeVhdlTag (name, VHDLTAG_PACKAGE);
|
||||
}
|
||||
else if (isType (token, TOKEN_IDENTIFIER))
|
||||
{
|
||||
makeVhdlTag (token, VHDLTAG_PACKAGE);
|
||||
}
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseModule (tokenInfo * const token)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
const vhdlKind kind = isKeyword (token, KEYWORD_ENTITY) ?
|
||||
VHDLTAG_ENTITY : VHDLTAG_COMPONENT;
|
||||
Assert (isKeyword (token, KEYWORD_ENTITY) ||
|
||||
isKeyword (token, KEYWORD_COMPONENT));
|
||||
readToken (name);
|
||||
if (kind == VHDLTAG_COMPONENT)
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_COMPONENT);
|
||||
skipToKeyword (KEYWORD_END);
|
||||
fileSkipToCharacter (';');
|
||||
}
|
||||
else
|
||||
{
|
||||
readToken (token);
|
||||
if (isKeyword (token, KEYWORD_IS))
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_ENTITY);
|
||||
skipToKeyword (KEYWORD_END);
|
||||
fileSkipToCharacter (';');
|
||||
}
|
||||
}
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseRecord (tokenInfo * const token)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
Assert (isKeyword (token, KEYWORD_RECORD));
|
||||
readToken (name);
|
||||
do
|
||||
{
|
||||
readToken (token); /* should be a colon */
|
||||
fileSkipToCharacter (';');
|
||||
makeVhdlTag (name, VHDLTAG_RECORD);
|
||||
readToken (name);
|
||||
}
|
||||
while (!isKeyword (name, KEYWORD_END));
|
||||
fileSkipToCharacter (';');
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseTypes (tokenInfo * const token)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
const vhdlKind kind = isKeyword (token, KEYWORD_TYPE) ?
|
||||
VHDLTAG_TYPE : VHDLTAG_SUBTYPE;
|
||||
Assert (isKeyword (token, KEYWORD_TYPE) ||
|
||||
isKeyword (token, KEYWORD_SUBTYPE));
|
||||
readToken (name);
|
||||
readToken (token);
|
||||
if (isKeyword (token, KEYWORD_IS))
|
||||
{
|
||||
readToken (token); /* type */
|
||||
if (isKeyword (token, KEYWORD_RECORD))
|
||||
{
|
||||
makeVhdlTag (name, kind);
|
||||
/*TODO: make tags of the record's names */
|
||||
parseRecord (token);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeVhdlTag (name, kind);
|
||||
}
|
||||
}
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseConstant (boolean local)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
readToken (name);
|
||||
if (local)
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_LOCAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_CONSTANT);
|
||||
}
|
||||
fileSkipToCharacter (';');
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
static void parseSubProgram (tokenInfo * const token)
|
||||
{
|
||||
tokenInfo *const name = newToken ();
|
||||
boolean endSubProgram = FALSE;
|
||||
const vhdlKind kind = isKeyword (token, KEYWORD_FUNCTION) ?
|
||||
VHDLTAG_FUNCTION : VHDLTAG_PROCEDURE;
|
||||
Assert (isKeyword (token, KEYWORD_FUNCTION) ||
|
||||
isKeyword (token, KEYWORD_PROCEDURE));
|
||||
readToken (name); /* the name of the function or procedure */
|
||||
readToken (token);
|
||||
if (isType (token, TOKEN_OPEN_PAREN))
|
||||
{
|
||||
skipToMatched (token);
|
||||
}
|
||||
|
||||
if (kind == VHDLTAG_FUNCTION)
|
||||
{
|
||||
if (isKeyword (token, KEYWORD_RETURN))
|
||||
{
|
||||
/* Read datatype */
|
||||
readToken (token);
|
||||
while (! isKeyword (token, KEYWORD_IS) &&
|
||||
! isType (token, TOKEN_SEMICOLON))
|
||||
{
|
||||
readToken (token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isType (token, TOKEN_SEMICOLON))
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_PROTOTYPE);
|
||||
}
|
||||
else if (isKeyword (token, KEYWORD_IS))
|
||||
{
|
||||
if (kind == VHDLTAG_FUNCTION)
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_FUNCTION);
|
||||
do
|
||||
{
|
||||
readToken (token);
|
||||
if (isKeyword (token, KEYWORD_END))
|
||||
{
|
||||
readToken (token);
|
||||
endSubProgram = isKeywordOrIdent (token,
|
||||
KEYWORD_FUNCTION, name->string);
|
||||
fileSkipToCharacter (';');
|
||||
}
|
||||
else
|
||||
{
|
||||
parseKeywords (token, TRUE);
|
||||
}
|
||||
} while (!endSubProgram);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeVhdlTag (name, VHDLTAG_PROCEDURE);
|
||||
do
|
||||
{
|
||||
readToken (token);
|
||||
if (isKeyword (token, KEYWORD_END))
|
||||
{
|
||||
readToken (token);
|
||||
endSubProgram = isKeywordOrIdent (token,
|
||||
KEYWORD_PROCEDURE, name->string);
|
||||
fileSkipToCharacter (';');
|
||||
}
|
||||
else
|
||||
{
|
||||
parseKeywords (token, TRUE);
|
||||
}
|
||||
} while (!endSubProgram);
|
||||
}
|
||||
}
|
||||
deleteToken (name);
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
/* records */
|
||||
static void parseKeywords (tokenInfo * const token, boolean local)
|
||||
{
|
||||
switch (token->keyword)
|
||||
{
|
||||
case KEYWORD_END:
|
||||
fileSkipToCharacter (';');
|
||||
break;
|
||||
case KEYWORD_CONSTANT:
|
||||
parseConstant (local);
|
||||
break;
|
||||
case KEYWORD_TYPE:
|
||||
parseTypes (token);
|
||||
break;
|
||||
case KEYWORD_SUBTYPE:
|
||||
parseTypes (token);
|
||||
break;
|
||||
case KEYWORD_ENTITY:
|
||||
parseModule (token);
|
||||
break;
|
||||
case KEYWORD_COMPONENT:
|
||||
parseModule (token);
|
||||
break;
|
||||
case KEYWORD_FUNCTION:
|
||||
parseSubProgram (token);
|
||||
break;
|
||||
case KEYWORD_PROCEDURE:
|
||||
parseSubProgram (token);
|
||||
break;
|
||||
case KEYWORD_PACKAGE:
|
||||
parsePackage (token);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void parseVhdlFile (tokenInfo * const token)
|
||||
{
|
||||
do
|
||||
{
|
||||
readToken (token);
|
||||
parseKeywords (token, FALSE);
|
||||
} while (!isKeyword (token, KEYWORD_END));
|
||||
}
|
||||
|
||||
static void findVhdlTags (void)
|
||||
{
|
||||
tokenInfo *const token = newToken ();
|
||||
exception_t exception = (exception_t) (setjmp (Exception));
|
||||
|
||||
while (exception == ExceptionNone)
|
||||
parseVhdlFile (token);
|
||||
|
||||
deleteToken (token);
|
||||
}
|
||||
|
||||
extern parserDefinition *VhdlParser (void)
|
||||
{
|
||||
static const char *const extensions[] = { "vhdl", "vhd", NULL };
|
||||
parserDefinition *def = parserNew ("VHDL");
|
||||
def->kinds = VhdlKinds;
|
||||
def->kindCount = KIND_COUNT (VhdlKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findVhdlTags;
|
||||
def->initialize = initialize;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4 noet: */
|
658
third_party/ctags/vim.c
vendored
Normal file
658
third_party/ctags/vim.c
vendored
Normal file
|
@ -0,0 +1,658 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: vim.c 762 2010-07-28 11:38:19Z dfishburn $
|
||||
*
|
||||
* Copyright (c) 2000-2003, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Thanks are due to Jay Glanville for significant improvements.
|
||||
*
|
||||
* This module contains functions for generating tags for user-defined
|
||||
* functions for the Vim editor.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#ifdef DEBUG
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/mem/fmt.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "third_party/musl/tempnam.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include "third_party/ctags/parse.h"
|
||||
#include "third_party/ctags/read.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
#if 0
|
||||
typedef struct sLineInfo {
|
||||
tokenType type;
|
||||
keywordId keyword;
|
||||
vString * string;
|
||||
vString * scope;
|
||||
unsigned long lineNumber;
|
||||
fpos_t filePosition;
|
||||
} lineInfo;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_AUGROUP,
|
||||
K_COMMAND,
|
||||
K_FUNCTION,
|
||||
K_MAP,
|
||||
K_VARIABLE
|
||||
} vimKind;
|
||||
|
||||
static kindOption VimKinds [] = {
|
||||
{ TRUE, 'a', "augroup", "autocommand groups" },
|
||||
{ TRUE, 'c', "command", "user-defined commands" },
|
||||
{ TRUE, 'f', "function", "function definitions" },
|
||||
{ TRUE, 'm', "map", "maps" },
|
||||
{ TRUE, 'v', "variable", "variable definitions" },
|
||||
};
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
#if 0
|
||||
typedef enum eException {
|
||||
ExceptionNone, ExceptionEOF
|
||||
} exception_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
|
||||
#if 0
|
||||
static jmp_buf Exception;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
/* This function takes a char pointer, tries to find a scope separator in the
|
||||
* string, and if it does, returns a pointer to the character after the colon,
|
||||
* and the character defining the scope.
|
||||
* If a colon is not found, it returns the original pointer.
|
||||
*/
|
||||
static const unsigned char* skipPrefix (const unsigned char* name, int *scope)
|
||||
{
|
||||
const unsigned char* result = name;
|
||||
int counter;
|
||||
size_t length;
|
||||
length = strlen((const char*)name);
|
||||
if (scope != NULL)
|
||||
*scope = '\0';
|
||||
if (length > 3 && name[1] == ':')
|
||||
{
|
||||
if (scope != NULL)
|
||||
*scope = *name;
|
||||
result = name + 2;
|
||||
}
|
||||
else if (length > 5 && strncasecmp ((const char*) name, "<SID>", (size_t) 5) == 0)
|
||||
{
|
||||
if (scope != NULL)
|
||||
*scope = *name;
|
||||
result = name + 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Vim7 check for dictionaries or autoload function names
|
||||
*/
|
||||
counter = 0;
|
||||
do
|
||||
{
|
||||
switch ( name[counter] )
|
||||
{
|
||||
case '.':
|
||||
/* Set the scope to d - Dictionary */
|
||||
*scope = 'd';
|
||||
break;
|
||||
case '#':
|
||||
/* Set the scope to a - autoload */
|
||||
*scope = 'a';
|
||||
break;
|
||||
}
|
||||
++counter;
|
||||
} while (isalnum ((int) name[counter]) ||
|
||||
name[counter] == '_' ||
|
||||
name[counter] == '.' ||
|
||||
name[counter] == '#'
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static boolean isMap (const unsigned char* line)
|
||||
{
|
||||
/*
|
||||
* There are many different short cuts for specifying a map.
|
||||
* This routine should capture all the permutations.
|
||||
*/
|
||||
if (
|
||||
strncmp ((const char*) line, "map", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "nm", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "nma", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "nmap", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "vm", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "vma", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "vmap", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "om", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "oma", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "omap", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "im", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "ima", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "imap", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "lm", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "lma", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "lmap", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "cm", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "cma", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "cmap", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "no", (size_t) 2) == 0 ||
|
||||
strncmp ((const char*) line, "nor", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "nore", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "norem", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "norema", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "noremap", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "nno", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "nnor", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "nnore", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "nnorem", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "nnorema", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "nnoremap", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) line, "vno", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "vnor", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "vnore", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "vnorem", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "vnorema", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "vnoremap", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) line, "ono", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "onor", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "onore", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "onorem", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "onorema", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "onoremap", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) line, "ino", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "inor", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "inore", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "inorem", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "inorema", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "inoremap", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) line, "lno", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "lnor", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "lnore", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "lnorem", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "lnorema", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "lnoremap", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) line, "cno", (size_t) 3) == 0 ||
|
||||
strncmp ((const char*) line, "cnor", (size_t) 4) == 0 ||
|
||||
strncmp ((const char*) line, "cnore", (size_t) 5) == 0 ||
|
||||
strncmp ((const char*) line, "cnorem", (size_t) 6) == 0 ||
|
||||
strncmp ((const char*) line, "cnorema", (size_t) 7) == 0 ||
|
||||
strncmp ((const char*) line, "cnoremap", (size_t) 8) == 0
|
||||
)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const unsigned char * readVimLine (void)
|
||||
{
|
||||
const unsigned char *line;
|
||||
|
||||
while ((line = fileReadLine ()) != NULL)
|
||||
{
|
||||
while (isspace ((int) *line))
|
||||
++line;
|
||||
|
||||
if ((int) *line == '"')
|
||||
continue; /* skip comment */
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
static void parseFunction (const unsigned char *line)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
/* boolean inFunction = FALSE; */
|
||||
int scope;
|
||||
|
||||
const unsigned char *cp = line + 1;
|
||||
|
||||
if ((int) *++cp == 'n' && (int) *++cp == 'c' &&
|
||||
(int) *++cp == 't' && (int) *++cp == 'i' &&
|
||||
(int) *++cp == 'o' && (int) *++cp == 'n')
|
||||
++cp;
|
||||
if ((int) *cp == '!')
|
||||
++cp;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (*cp && isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (*cp)
|
||||
{
|
||||
cp = skipPrefix (cp, &scope);
|
||||
if (isupper ((int) *cp) ||
|
||||
scope == 's' || /* script scope */
|
||||
scope == '<' || /* script scope */
|
||||
scope == 'd' || /* dictionary */
|
||||
scope == 'a') /* autoload */
|
||||
{
|
||||
do
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
} while (isalnum ((int) *cp) || *cp == '_' || *cp == '.' || *cp == '#');
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, VimKinds, K_FUNCTION);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO - update struct to indicate inside function */
|
||||
while ((line = readVimLine ()) != NULL)
|
||||
{
|
||||
/*
|
||||
* Vim7 added the for/endfo[r] construct, so we must first
|
||||
* check for an "endfo", before a "endf"
|
||||
*/
|
||||
if ( (!strncmp ((const char*) line, "endfo", (size_t) 5) == 0) &&
|
||||
(strncmp ((const char*) line, "endf", (size_t) 4) == 0) )
|
||||
break;
|
||||
/* TODO - call parseVimLine */
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
static void parseAutogroup (const unsigned char *line)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
|
||||
/* Found Autocommand Group (augroup) */
|
||||
const unsigned char *cp = line + 2;
|
||||
if ((int) *++cp == 'r' && (int) *++cp == 'o' &&
|
||||
(int) *++cp == 'u' && (int) *++cp == 'p')
|
||||
++cp;
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (*cp && isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (*cp)
|
||||
{
|
||||
if (strncasecmp ((const char*) cp, "end", (size_t) 3) != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
} while (isalnum ((int) *cp) || *cp == '_');
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, VimKinds, K_AUGROUP);
|
||||
vStringClear (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
static boolean parseCommand (const unsigned char *line)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
boolean cmdProcessed = TRUE;
|
||||
|
||||
/*
|
||||
* Found a user-defined command
|
||||
*
|
||||
* They can have many options preceeded by a dash
|
||||
* command! -nargs=+ -complete Select :call s:DB_execSql("select " . <q-args>)
|
||||
* The name of the command should be the first word not preceeded by a dash
|
||||
*
|
||||
*/
|
||||
const unsigned char *cp = line;
|
||||
|
||||
if ( (int) *cp == '\\' )
|
||||
{
|
||||
/*
|
||||
* We are recursively calling this function is the command
|
||||
* has been continued on to the next line
|
||||
*
|
||||
* Vim statements can be continued onto a newline using a \
|
||||
* to indicate the previous line is continuing.
|
||||
*
|
||||
* com -nargs=1 -bang -complete=customlist,EditFileComplete
|
||||
* \ EditFile edit<bang> <args>
|
||||
*
|
||||
* If the following lines do not have a line continuation
|
||||
* the command must not be spanning multiple lines and should
|
||||
* be synatically incorrect.
|
||||
*/
|
||||
if ((int) *cp == '\\')
|
||||
++cp;
|
||||
|
||||
while (*cp && isspace ((int) *cp))
|
||||
++cp;
|
||||
}
|
||||
else if ( (!strncmp ((const char*) line, "comp", (size_t) 4) == 0) &&
|
||||
(!strncmp ((const char*) line, "comc", (size_t) 4) == 0) &&
|
||||
(strncmp ((const char*) line, "com", (size_t) 3) == 0) )
|
||||
{
|
||||
cp += 2;
|
||||
if ((int) *++cp == 'm' && (int) *++cp == 'a' &&
|
||||
(int) *++cp == 'n' && (int) *++cp == 'd')
|
||||
++cp;
|
||||
|
||||
if ((int) *cp == '!')
|
||||
++cp;
|
||||
|
||||
if ((int) *cp != ' ')
|
||||
{
|
||||
/*
|
||||
* :command must be followed by a space. If it is not, it is
|
||||
* not a valid command.
|
||||
* Treat the line as processed and continue.
|
||||
*/
|
||||
cmdProcessed = TRUE;
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
while (*cp && isspace ((int) *cp))
|
||||
++cp;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We are recursively calling this function. If it does not start
|
||||
* with "com" or a line continuation character, we have moved off
|
||||
* the command line and should let the other routines parse this file.
|
||||
*/
|
||||
cmdProcessed = FALSE;
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strip off any spaces and options which are part of the command.
|
||||
* These should preceed the command name.
|
||||
*/
|
||||
do
|
||||
{
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
++cp;
|
||||
}
|
||||
else if (*cp == '-')
|
||||
{
|
||||
/*
|
||||
* Read until the next space which separates options or the name
|
||||
*/
|
||||
while (*cp && !isspace ((int) *cp))
|
||||
++cp;
|
||||
}
|
||||
else
|
||||
break;
|
||||
} while ( *cp );
|
||||
|
||||
if ( ! *cp )
|
||||
{
|
||||
/*
|
||||
* We have reached the end of the line without finding the command name.
|
||||
* Read the next line and continue processing it as a command.
|
||||
*/
|
||||
line = readVimLine();
|
||||
parseCommand(line);
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
} while (isalnum ((int) *cp) || *cp == '_');
|
||||
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, VimKinds, K_COMMAND);
|
||||
vStringClear (name);
|
||||
|
||||
cleanUp:
|
||||
vStringDelete (name);
|
||||
|
||||
return cmdProcessed;
|
||||
}
|
||||
|
||||
static void parseLet (const unsigned char *line)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
|
||||
/* we've found a variable declared outside of a function!! */
|
||||
const unsigned char *cp = line + 3;
|
||||
const unsigned char *np = line;
|
||||
/* get the name */
|
||||
if (isspace ((int) *cp))
|
||||
{
|
||||
while (*cp && isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
/*
|
||||
* Ignore lets which set:
|
||||
* & - local buffer vim settings
|
||||
* @ - registers
|
||||
* [ - Lists or Dictionaries
|
||||
*/
|
||||
if (!*cp || *cp == '&' || *cp == '@' || *cp == '[' )
|
||||
goto cleanUp;
|
||||
|
||||
/*
|
||||
* Ignore vim variables which are read only
|
||||
* v: - Vim variables.
|
||||
*/
|
||||
np = cp;
|
||||
++np;
|
||||
if ((int) *cp == 'v' && (int) *np == ':' )
|
||||
goto cleanUp;
|
||||
|
||||
/* deal with spaces, $, @ and & */
|
||||
while (*cp && *cp != '$' && !isalnum ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (!*cp)
|
||||
goto cleanUp;
|
||||
|
||||
/* cp = skipPrefix (cp, &scope); */
|
||||
do
|
||||
{
|
||||
if (!*cp)
|
||||
break;
|
||||
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
} while (isalnum ((int) *cp) || *cp == '_' || *cp == '#' || *cp == ':' || *cp == '$');
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, VimKinds, K_VARIABLE);
|
||||
vStringClear (name);
|
||||
}
|
||||
|
||||
cleanUp:
|
||||
vStringDelete (name);
|
||||
}
|
||||
|
||||
static boolean parseMap (const unsigned char *line)
|
||||
{
|
||||
vString *name = vStringNew ();
|
||||
|
||||
const unsigned char *cp = line;
|
||||
|
||||
/* Remove map */
|
||||
while (*cp && isalnum ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if ((int) *cp == '!')
|
||||
++cp;
|
||||
|
||||
/*
|
||||
* Maps follow this basic format
|
||||
* map
|
||||
* nnoremap <silent> <F8> :Tlist<CR>
|
||||
* map <unique> <Leader>scdt <Plug>GetColumnDataType
|
||||
* inoremap ,,, <esc>diwi<<esc>pa><cr></<esc>pa><esc>kA
|
||||
* inoremap <buffer> ( <C-R>=PreviewFunctionSignature()<LF>
|
||||
*
|
||||
* The Vim help shows the various special arguments available to a map:
|
||||
* 1.2 SPECIAL ARGUMENTS *:map-arguments*
|
||||
* <buffer>
|
||||
* <silent>
|
||||
* <script>
|
||||
* <unique>
|
||||
* <special>
|
||||
* <expr>
|
||||
*
|
||||
* Strip the special arguments from the map command, this should leave
|
||||
* the map name which we will use as the "name".
|
||||
*/
|
||||
|
||||
do
|
||||
{
|
||||
while (*cp && isspace ((int) *cp))
|
||||
++cp;
|
||||
|
||||
if (strncmp ((const char*) cp, "<Leader>", (size_t) 8) == 0)
|
||||
break;
|
||||
|
||||
if (
|
||||
strncmp ((const char*) cp, "<buffer>", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) cp, "<silent>", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) cp, "<script>", (size_t) 8) == 0 ||
|
||||
strncmp ((const char*) cp, "<unique>", (size_t) 8) == 0
|
||||
)
|
||||
{
|
||||
cp += 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp ((const char*) cp, "<expr>", (size_t) 6) == 0)
|
||||
{
|
||||
cp += 6;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp ((const char*) cp, "<special>", (size_t) 9) == 0)
|
||||
{
|
||||
cp += 9;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while (*cp);
|
||||
|
||||
do
|
||||
{
|
||||
vStringPut (name, (int) *cp);
|
||||
++cp;
|
||||
} while (*cp && *cp != ' ');
|
||||
|
||||
vStringTerminate (name);
|
||||
makeSimpleTag (name, VimKinds, K_MAP);
|
||||
vStringClear (name);
|
||||
|
||||
vStringDelete (name);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static boolean parseVimLine (const unsigned char *line)
|
||||
{
|
||||
boolean readNextLine = TRUE;
|
||||
|
||||
if ( (!strncmp ((const char*) line, "comp", (size_t) 4) == 0) &&
|
||||
(!strncmp ((const char*) line, "comc", (size_t) 4) == 0) &&
|
||||
(strncmp ((const char*) line, "com", (size_t) 3) == 0) )
|
||||
{
|
||||
readNextLine = parseCommand(line);
|
||||
/* TODO - Handle parseCommand returning FALSE */
|
||||
}
|
||||
|
||||
if (isMap(line))
|
||||
{
|
||||
parseMap(line);
|
||||
}
|
||||
|
||||
if (strncmp ((const char*) line, "fu", (size_t) 2) == 0)
|
||||
{
|
||||
parseFunction(line);
|
||||
}
|
||||
|
||||
if (strncmp ((const char*) line, "aug", (size_t) 3) == 0)
|
||||
{
|
||||
parseAutogroup(line);
|
||||
}
|
||||
|
||||
if ( strncmp ((const char*) line, "let", (size_t) 3) == 0 )
|
||||
{
|
||||
parseLet(line);
|
||||
}
|
||||
|
||||
return readNextLine;
|
||||
}
|
||||
|
||||
static void parseVimFile (const unsigned char *line)
|
||||
{
|
||||
boolean readNextLine = TRUE;
|
||||
line = readVimLine();
|
||||
|
||||
while (line != NULL)
|
||||
{
|
||||
readNextLine = parseVimLine(line);
|
||||
|
||||
if ( readNextLine )
|
||||
line = readVimLine();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void findVimTags (void)
|
||||
{
|
||||
const unsigned char *line;
|
||||
/* TODO - change this into a structure */
|
||||
|
||||
line = '\0';
|
||||
|
||||
parseVimFile (line);
|
||||
}
|
||||
|
||||
extern parserDefinition* VimParser (void)
|
||||
{
|
||||
static const char *const extensions [] = { "vim", NULL };
|
||||
parserDefinition* def = parserNew ("Vim");
|
||||
def->kinds = VimKinds;
|
||||
def->kindCount = KIND_COUNT (VimKinds);
|
||||
def->extensions = extensions;
|
||||
def->parser = findVimTags;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
|
239
third_party/ctags/vstring.c
vendored
Normal file
239
third_party/ctags/vstring.c
vendored
Normal file
|
@ -0,0 +1,239 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: vstring.c 558 2007-06-15 19:17:02Z elliotth $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* This module contains functions supporting resizeable strings.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/limits.h"
|
||||
#include "libc/sysv/consts/_posix.h"
|
||||
#include "libc/sysv/consts/iov.h"
|
||||
#include "libc/sysv/consts/limits.h"
|
||||
#include "libc/sysv/consts/xopen.h"
|
||||
#include "libc/thread/thread.h" /* to define INT_MAX */
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#include "third_party/ctags/debug.h"
|
||||
#include "third_party/ctags/routines.h"
|
||||
#include "third_party/ctags/vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
static const size_t vStringInitialSize = 32;
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void vStringResize (vString *const string, const size_t newSize)
|
||||
{
|
||||
char *const newBuffer = xRealloc (string->buffer, newSize, char);
|
||||
|
||||
string->size = newSize;
|
||||
string->buffer = newBuffer;
|
||||
}
|
||||
|
||||
/*
|
||||
* External interface
|
||||
*/
|
||||
|
||||
extern boolean vStringAutoResize (vString *const string)
|
||||
{
|
||||
boolean ok = TRUE;
|
||||
|
||||
if (string->size <= INT_MAX / 2)
|
||||
{
|
||||
const size_t newSize = string->size * 2;
|
||||
|
||||
vStringResize (string, newSize);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
extern void vStringClear (vString *const string)
|
||||
{
|
||||
string->length = 0;
|
||||
string->buffer [0] = '\0';
|
||||
DebugStatement ( memset (string->buffer, 0, string->size); )
|
||||
}
|
||||
|
||||
extern void vStringDelete (vString *const string)
|
||||
{
|
||||
if (string != NULL)
|
||||
{
|
||||
if (string->buffer != NULL)
|
||||
eFree (string->buffer);
|
||||
eFree (string);
|
||||
}
|
||||
}
|
||||
|
||||
extern vString *vStringNew (void)
|
||||
{
|
||||
vString *const string = xMalloc (1, vString);
|
||||
|
||||
string->length = 0;
|
||||
string->size = vStringInitialSize;
|
||||
string->buffer = xMalloc (string->size, char);
|
||||
|
||||
vStringClear (string);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
#ifndef VSTRING_PUTC_MACRO
|
||||
extern void vStringPut (vString *const string, const int c)
|
||||
{
|
||||
if (string->length + 1 == string->size) /* check for buffer overflow */
|
||||
vStringAutoResize (string);
|
||||
|
||||
string->buffer [string->length] = c;
|
||||
if (c != '\0')
|
||||
string->buffer [++string->length] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void vStringCatS (vString *const string, const char *const s)
|
||||
{
|
||||
#if 1
|
||||
const size_t len = strlen (s);
|
||||
while (string->length + len + 1 >= string->size)/* check for buffer overflow */
|
||||
vStringAutoResize (string);
|
||||
strcpy (string->buffer + string->length, s);
|
||||
string->length += len;
|
||||
#else
|
||||
const char *p = s;
|
||||
do
|
||||
vStringPut (string, *p);
|
||||
while (*p++ != '\0');
|
||||
#endif
|
||||
}
|
||||
|
||||
extern vString *vStringNewCopy (const vString *const string)
|
||||
{
|
||||
vString *vs = vStringNew ();
|
||||
vStringCatS (vs, string->buffer);
|
||||
return vs;
|
||||
}
|
||||
|
||||
extern vString *vStringNewInit (const char *const s)
|
||||
{
|
||||
vString *vs = vStringNew ();
|
||||
vStringCatS (vs, s);
|
||||
return vs;
|
||||
}
|
||||
|
||||
extern void vStringNCatS (
|
||||
vString *const string, const char *const s, const size_t length)
|
||||
{
|
||||
const char *p = s;
|
||||
size_t remain = length;
|
||||
|
||||
while (*p != '\0' && remain > 0)
|
||||
{
|
||||
vStringPut (string, *p);
|
||||
--remain;
|
||||
++p;
|
||||
}
|
||||
vStringTerminate (string);
|
||||
}
|
||||
|
||||
/* Strip trailing newline from string.
|
||||
*/
|
||||
extern void vStringStripNewline (vString *const string)
|
||||
{
|
||||
const size_t final = string->length - 1;
|
||||
if (string->buffer [final] == '\n')
|
||||
{
|
||||
string->buffer [final] = '\0';
|
||||
string->length--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip leading white space from string.
|
||||
*/
|
||||
extern void vStringStripLeading (vString *const string)
|
||||
{
|
||||
while (isspace ((int) string->buffer [0]) && string->length > 0)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 1 ; i < string->length ; ++i)
|
||||
string->buffer [i - 1] = string->buffer [i];
|
||||
--string->length;
|
||||
string->buffer [string->length] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip trailing white space from string.
|
||||
*/
|
||||
extern void vStringStripTrailing (vString *const string)
|
||||
{
|
||||
while (isspace ((int) string->buffer [string->length - 1]) &&
|
||||
string->length > 0)
|
||||
{
|
||||
string->length--;
|
||||
string->buffer [string->length] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Chop last character from string.
|
||||
*/
|
||||
extern void vStringChop (vString *const string)
|
||||
{
|
||||
if (string->length > 0)
|
||||
{
|
||||
--string->length;
|
||||
string->buffer [string->length] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
extern void vStringCopyS (vString *const string, const char *const s)
|
||||
{
|
||||
vStringClear (string);
|
||||
vStringCatS (string, s);
|
||||
}
|
||||
|
||||
extern void vStringNCopyS (
|
||||
vString *const string, const char *const s, const size_t length)
|
||||
{
|
||||
vStringClear (string);
|
||||
vStringNCatS (string, s, length);
|
||||
}
|
||||
|
||||
extern void vStringCopyToLower (vString *const dest, const vString *const src)
|
||||
{
|
||||
const size_t length = src->length;
|
||||
const char *s = src->buffer;
|
||||
char *d;
|
||||
size_t i;
|
||||
|
||||
if (dest->size < src->size)
|
||||
vStringResize (dest, src->size);
|
||||
d = dest->buffer;
|
||||
for (i = 0 ; i < length ; ++i)
|
||||
{
|
||||
int c = s [i];
|
||||
|
||||
d [i] = tolower (c);
|
||||
}
|
||||
d [i] = '\0';
|
||||
}
|
||||
|
||||
extern void vStringSetLength (vString *const string)
|
||||
{
|
||||
string->length = strlen (string->buffer);
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
101
third_party/ctags/vstring.h
vendored
Normal file
101
third_party/ctags/vstring.h
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: vstring.h 719 2009-07-07 03:46:59Z dhiebert $
|
||||
*
|
||||
* Copyright (c) 1998-2002, Darren Hiebert
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Provides the external interface for resizeable strings.
|
||||
*/
|
||||
#ifndef _VSTRING_H
|
||||
#define _VSTRING_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#if defined(HAVE_STDLIB_H)
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/dprintf.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/musl/crypt.h"
|
||||
#include "third_party/musl/rand48.h" /* to define size_t */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
#ifndef DEBUG
|
||||
# define VSTRING_PUTC_MACRO 1
|
||||
#endif
|
||||
#ifdef VSTRING_PUTC_MACRO
|
||||
#define vStringPut(s,c) \
|
||||
(void)(((s)->length + 1 == (s)->size ? vStringAutoResize (s) : 0), \
|
||||
((s)->buffer [(s)->length] = (c)), \
|
||||
((c) == '\0' ? 0 : ((s)->buffer [++(s)->length] = '\0')))
|
||||
#endif
|
||||
|
||||
#define vStringValue(vs) ((vs)->buffer)
|
||||
#define vStringItem(vs,i) ((vs)->buffer[i])
|
||||
#define vStringLast(vs) ((vs)->buffer[(vs)->length - 1])
|
||||
#define vStringLength(vs) ((vs)->length)
|
||||
#define vStringSize(vs) ((vs)->size)
|
||||
#define vStringCat(vs,s) vStringCatS((vs), vStringValue((s)))
|
||||
#define vStringNCat(vs,s,l) vStringNCatS((vs), vStringValue((s)), (l))
|
||||
#define vStringCopy(vs,s) vStringCopyS((vs), vStringValue((s)))
|
||||
#define vStringNCopy(vs,s,l) vStringNCopyS((vs), vStringValue((s)), (l))
|
||||
#define vStringChar(vs,i) ((vs)->buffer[i])
|
||||
#define vStringTerminate(vs) vStringPut(vs, '\0')
|
||||
#define vStringLower(vs) toLowerString((vs)->buffer)
|
||||
#define vStringUpper(vs) toUpperString((vs)->buffer)
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
|
||||
typedef struct sVString {
|
||||
size_t length; /* size of buffer used */
|
||||
size_t size; /* allocated size of buffer */
|
||||
char *buffer; /* location of buffer */
|
||||
} vString;
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern boolean vStringAutoResize (vString *const string);
|
||||
extern void vStringClear (vString *const string);
|
||||
extern vString *vStringNew (void);
|
||||
extern void vStringDelete (vString *const string);
|
||||
#ifndef VSTRING_PUTC_MACRO
|
||||
extern void vStringPut (vString *const string, const int c);
|
||||
#endif
|
||||
extern void vStringStripNewline (vString *const string);
|
||||
extern void vStringStripLeading (vString *const string);
|
||||
extern void vStringChop (vString *const string);
|
||||
extern void vStringStripTrailing (vString *const string);
|
||||
extern void vStringCatS (vString *const string, const char *const s);
|
||||
extern void vStringNCatS (vString *const string, const char *const s, const size_t length);
|
||||
extern vString *vStringNewCopy (const vString *const string);
|
||||
extern vString *vStringNewInit (const char *const s);
|
||||
extern void vStringCopyS (vString *const string, const char *const s);
|
||||
extern void vStringNCopyS (vString *const string, const char *const s, const size_t length);
|
||||
extern void vStringCopyToLower (vString *const dest, const vString *const src);
|
||||
extern void vStringSetLength (vString *const string);
|
||||
|
||||
#endif /* _VSTRING_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
42
third_party/ctags/yacc.c
vendored
Normal file
42
third_party/ctags/yacc.c
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// clang-format off
|
||||
/*
|
||||
* $Id: yacc.c 443 2006-05-30 04:37:13Z darren $
|
||||
*
|
||||
* Copyright (c) 2001-2002, Nick Hibma <n_hibma@van-laarhoven.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 YACC language files.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "third_party/ctags/general.h" /* must always come first */
|
||||
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/ctags/parse.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
static void installYaccRegex (const langType language)
|
||||
{
|
||||
addTagRegex (language,
|
||||
"^([A-Za-z][A-Za-z_0-9]+)[ \t]*:", "\\1", "l,label,labels", NULL);
|
||||
}
|
||||
|
||||
extern parserDefinition* YaccParser ()
|
||||
{
|
||||
static const char *const extensions [] = { "y", NULL };
|
||||
parserDefinition* const def = parserNew ("YACC");
|
||||
def->extensions = extensions;
|
||||
def->initialize = installYaccRegex;
|
||||
def->regex = TRUE;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
1
third_party/third_party.mk
vendored
1
third_party/third_party.mk
vendored
|
@ -8,6 +8,7 @@ o/$(MODE)/third_party: \
|
|||
o/$(MODE)/third_party/bzip2 \
|
||||
o/$(MODE)/third_party/chibicc \
|
||||
o/$(MODE)/third_party/compiler_rt \
|
||||
o/$(MODE)/third_party/ctags \
|
||||
o/$(MODE)/third_party/dlmalloc \
|
||||
o/$(MODE)/third_party/finger \
|
||||
o/$(MODE)/third_party/gdtoa \
|
||||
|
|
Loading…
Reference in a new issue