Import exuberant ctags

This commit is contained in:
Justine Tunney 2022-11-11 14:22:42 -08:00
parent d55b019284
commit 711bd095db
No known key found for this signature in database
GPG Key ID: BE714B4575D6E328
79 changed files with 34966 additions and 9 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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
View 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
View 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, &current->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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

51
third_party/ctags/cobol.c vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

879
third_party/ctags/entry.c vendored Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

260
third_party/ctags/keyword.c vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

155
third_party/ctags/options.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

283
third_party/ctags/strlist.c vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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: */

View File

@ -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 \