Mercurial > hg > xemacs-beta
annotate lib-src/etags.c @ 5157:1fae11d56ad2
redo memory-usage mechanism, add way of dynamically initializing Lisp objects
-------------------- ChangeLog entries follow: --------------------
lisp/ChangeLog addition:
2010-03-18 Ben Wing <ben@xemacs.org>
* diagnose.el (show-memory-usage):
Rewrite to take into account API changes in memory-usage functions.
src/ChangeLog addition:
2010-03-18 Ben Wing <ben@xemacs.org>
* alloc.c:
* alloc.c (disksave_object_finalization_1):
* alloc.c (lisp_object_storage_size):
* alloc.c (listu):
* alloc.c (listn):
* alloc.c (Fobject_memory_usage_stats):
* alloc.c (compute_memusage_stats_length):
* alloc.c (Fobject_memory_usage):
* alloc.c (Ftotal_object_memory_usage):
* alloc.c (malloced_storage_size):
* alloc.c (common_init_alloc_early):
* alloc.c (reinit_alloc_objects_early):
* alloc.c (reinit_alloc_early):
* alloc.c (init_alloc_once_early):
* alloc.c (syms_of_alloc):
* alloc.c (reinit_vars_of_alloc):
* buffer.c:
* buffer.c (struct buffer_stats):
* buffer.c (compute_buffer_text_usage):
* buffer.c (compute_buffer_usage):
* buffer.c (buffer_memory_usage):
* buffer.c (buffer_objects_create):
* buffer.c (syms_of_buffer):
* buffer.c (vars_of_buffer):
* console-impl.h (struct console_methods):
* dynarr.c (Dynarr_memory_usage):
* emacs.c (main_1):
* events.c (clear_event_resource):
* extents.c:
* extents.c (compute_buffer_extent_usage):
* extents.c (extent_objects_create):
* extents.h:
* faces.c:
* faces.c (compute_face_cachel_usage):
* faces.c (face_objects_create):
* faces.h:
* general-slots.h:
* glyphs.c:
* glyphs.c (compute_glyph_cachel_usage):
* glyphs.c (glyph_objects_create):
* glyphs.h:
* lisp.h:
* lisp.h (struct usage_stats):
* lrecord.h:
* lrecord.h (enum lrecord_type):
* lrecord.h (struct lrecord_implementation):
* lrecord.h (MC_ALLOC_CALL_FINALIZER_FOR_DISKSAVE):
* lrecord.h (DEFINE_DUMPABLE_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_SIZABLE_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_FROB_BLOCK_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_FROB_BLOCK_SIZABLE_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_INTERNAL_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_SIZABLE_INTERNAL_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_SIZABLE_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_FROB_BLOCK_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_FROB_BLOCK_SIZABLE_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_INTERNAL_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_SIZABLE_INTERNAL_LISP_OBJECT):
* lrecord.h (MAKE_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_MODULE_LISP_OBJECT):
* lrecord.h (DEFINE_DUMPABLE_MODULE_SIZABLE_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_MODULE_LISP_OBJECT):
* lrecord.h (DEFINE_NODUMP_MODULE_SIZABLE_LISP_OBJECT):
* lrecord.h (MAKE_MODULE_LISP_OBJECT):
* lrecord.h (INIT_LISP_OBJECT):
* lrecord.h (INIT_MODULE_LISP_OBJECT):
* lrecord.h (UNDEF_LISP_OBJECT):
* lrecord.h (UNDEF_MODULE_LISP_OBJECT):
* lrecord.h (DECLARE_LISP_OBJECT):
* lrecord.h (DECLARE_MODULE_API_LISP_OBJECT):
* lrecord.h (DECLARE_MODULE_LISP_OBJECT):
* lstream.c:
* lstream.c (syms_of_lstream):
* lstream.c (vars_of_lstream):
* marker.c:
* marker.c (compute_buffer_marker_usage):
* mc-alloc.c (mc_alloced_storage_size):
* mc-alloc.h:
* mule-charset.c:
* mule-charset.c (struct charset_stats):
* mule-charset.c (compute_charset_usage):
* mule-charset.c (charset_memory_usage):
* mule-charset.c (mule_charset_objects_create):
* mule-charset.c (syms_of_mule_charset):
* mule-charset.c (vars_of_mule_charset):
* redisplay.c:
* redisplay.c (compute_rune_dynarr_usage):
* redisplay.c (compute_display_block_dynarr_usage):
* redisplay.c (compute_glyph_block_dynarr_usage):
* redisplay.c (compute_display_line_dynarr_usage):
* redisplay.c (compute_line_start_cache_dynarr_usage):
* redisplay.h:
* scrollbar-gtk.c (gtk_compute_scrollbar_instance_usage):
* scrollbar-msw.c (mswindows_compute_scrollbar_instance_usage):
* scrollbar-x.c (x_compute_scrollbar_instance_usage):
* scrollbar.c (compute_scrollbar_instance_usage):
* scrollbar.h:
* symbols.c:
* symbols.c (reinit_symbol_objects_early):
* symbols.c (init_symbols_once_early):
* symbols.c (reinit_symbols_early):
* symbols.c (defsymbol_massage_name_1):
* symsinit.h:
* ui-gtk.c:
* ui-gtk.c (emacs_gtk_object_getprop):
* ui-gtk.c (emacs_gtk_object_putprop):
* ui-gtk.c (ui_gtk_objects_create):
* unicode.c (compute_from_unicode_table_size_1):
* unicode.c (compute_to_unicode_table_size_1):
* unicode.c (compute_from_unicode_table_size):
* unicode.c (compute_to_unicode_table_size):
* window.c:
* window.c (struct window_stats):
* window.c (compute_window_mirror_usage):
* window.c (compute_window_usage):
* window.c (window_memory_usage):
* window.c (window_objects_create):
* window.c (syms_of_window):
* window.c (vars_of_window):
* window.h:
Redo memory-usage mechanism, make it general; add way of dynamically
initializing Lisp object types -- OBJECT_HAS_METHOD(), similar to
CONSOLE_HAS_METHOD().
(1) Create OBJECT_HAS_METHOD(), OBJECT_HAS_PROPERTY() etc. for
specifying that a Lisp object type has a particular method or
property. Call such methods with OBJECT_METH, MAYBE_OBJECT_METH,
OBJECT_METH_OR_GIVEN; retrieve properties with OBJECT_PROPERTY.
Methods that formerly required a DEFINE_*GENERAL_LISP_OBJECT() to
specify them (getprop, putprop, remprop, plist, disksave) now
instead use the dynamic-method mechanism. The main benefit of
this is that new methods or properties can be added without
requiring that the declaration statements of all existing methods
be modified. We have to make the `struct lrecord_implementation'
non-const, but I don't think this should have any effect on speed --
the only possible method that's really speed-critical is the
mark method, and we already extract those out into a separate
(non-const) array for increased cache locality.
Object methods need to be reinitialized after pdump, so we put
them in separate functions such as face_objects_create(),
extent_objects_create() and call them appropriately from emacs.c
The only current object property (`memusage_stats_list') that
objects can specify is a Lisp object and gets staticpro()ed so it
only needs to be set during dump time, but because it references
symbols that might not exist in a syms_of_() function, we
initialize it in vars_of_(). There is also an object property
(`num_extra_memusage_stats') that is automatically initialized based
on `memusage_stats_list'; we do that in reinit_vars_of_alloc(),
which is called after all vars_of_() functions are called.
`disksaver' method was renamed `disksave' to correspond with the
name normally given to the function (e.g. disksave_lstream()).
(2) Generalize the memory-usage mechanism in `buffer-memory-usage',
`window-memory-usage', `charset-memory-usage' into an object-type-
specific mechanism called by a single function
`object-memory-usage'. (Former function `object-memory-usage'
renamed to `total-object-memory-usage'). Generalize the mechanism
of different "slices" so that we can have different "classes" of
memory described and different "slices" onto each class; `t'
separates classes, `nil' separates slices. Currently we have
three classes defined: the memory of an object itself,
non-Lisp-object memory associated with the object (e.g. arrays or
dynarrs stored as fields in the object), and Lisp-object memory
associated with the object (other internal Lisp objects stored in
the object). This isn't completely finished yet and we might need
to further separate the "other internal Lisp objects" class into
two classes.
The memory-usage mechanism uses a `struct usage_stats' (renamed
from `struct overhead_stats') to describe a malloc-view onto a set
of allocated memory (listing how much was requested and various
types of overhead) and a more general `struct generic_usage_stats'
(with a `struct usage_stats' in it) to hold all statistics about
object memory. `struct generic_usage_stats' contains an array of
32 Bytecounts, which are statistics of unspecified semantics. The
intention is that individual types declare a corresponding struct
(e.g. `struct window_stats') with the same structure but with
specific fields in place of the array, corresponding to specific
statistics. The number of such statistics is an object property
computed from the list of tags (Lisp symbols describing the
statistics) stored in `memusage_stats_list'. The idea here is to
allow particular object types to customize the number and
semantics of the statistics where completely avoiding consing.
This doesn't matter so much yet, but the intention is to have the
memory usage of all objects computed at the end of GC, at the same
time as other statistics are currently computed. The values for
all statistics for a single type would be added up to compute
aggregate values for all objects of a specific type. To make this
efficient, we can't allow any memory allocation at all.
(3) Create some additional functions for creating lists that
specify the elements directly as args rather than indirectly through
an array: listn() (number of args given), listu() (list terminated
by Qunbound).
(4) Delete a bit of remaining unused C window_config stuff, also
unused lrecord_type_popup_data.
author | Ben Wing <ben@xemacs.org> |
---|---|
date | Thu, 18 Mar 2010 10:50:06 -0500 |
parents | 0f5bee973a7b |
children | 061f4f90f874 |
rev | line source |
---|---|
458 | 1 /* Tags file maker to go with GNU Emacs -*- coding: latin-1 -*- |
3972 | 2 |
3 Copyright (C) 1984 The Regents of the University of California | |
4 | |
5 Redistribution and use in source and binary forms, with or without | |
6 modification, are permitted provided that the following conditions are | |
7 met: | |
8 1. Redistributions of source code must retain the above copyright | |
9 notice, this list of conditions and the following disclaimer. | |
10 2. Redistributions in binary form must reproduce the above copyright | |
11 notice, this list of conditions and the following disclaimer in the | |
12 documentation and/or other materials provided with the | |
13 distribution. | |
14 3. Neither the name of the University nor the names of its | |
15 contributors may be used to endorse or promote products derived | |
16 from this software without specific prior written permission. | |
17 | |
18 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' | |
19 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
21 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS | |
22 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
23 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
25 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
26 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
27 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | |
28 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | |
30 | |
31 Copyright (C) 1984, 1987, 1988, 1989, 1993, 1994, 1995, 1998, 1999, | |
32 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 | |
33 Free Software Foundation, Inc. | |
34 | |
35 This file is not considered part of GNU Emacs. | |
36 | |
37 This program is free software; you can redistribute it and/or modify | |
38 it under the terms of the GNU General Public License as published by | |
39 the Free Software Foundation; either version 2 of the License, or | |
40 (at your option) any later version. | |
41 | |
42 This program is distributed in the hope that it will be useful, | |
43 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
45 GNU General Public License for more details. | |
46 | |
47 You should have received a copy of the GNU General Public License | |
48 along with this program; if not, write to the Free Software Foundation, | |
49 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | |
50 | |
51 | |
52 /* NB To comply with the above BSD license, copyright information is | |
53 reproduced in etc/ETAGS.README. That file should be updated when the | |
54 above notices are. | |
55 | |
56 To the best of our knowledge, this code was originally based on the | |
57 ctags.c distributed with BSD4.2, which was copyrighted by the | |
58 University of California, as described above. */ | |
59 | |
428 | 60 |
61 /* | |
62 * Authors: | |
3972 | 63 * 1983 Ctags originally by Ken Arnold. |
64 * 1984 Fortran added by Jim Kleckner. | |
65 * 1984 Ed Pelegri-Llopart added C typedefs. | |
66 * 1985 Emacs TAGS format by Richard Stallman. | |
458 | 67 * 1989 Sam Kendall added C++. |
2225 | 68 * 1992 Joseph B. Wells improved C and C++ parsing. |
69 * 1993 Francesco Potort́ reorganised C and C++. | |
70 * 1994 Line-by-line regexp tags by Tom Tromey. | |
71 * 2001 Nested classes by Francesco Potort́ (concept by Mykola Dzyuba). | |
72 * 2002 #line directives by Francesco Potort́. | |
428 | 73 * |
2225 | 74 * Francesco Potort́ <pot@gnu.org> has maintained and improved it since 1993. |
428 | 75 */ |
76 | |
2325 | 77 /* |
78 * If you want to add support for a new language, start by looking at the LUA | |
79 * language, which is the simplest. Alternatively, consider shipping a | |
80 * configuration file containing regexp definitions for etags. | |
81 */ | |
82 | |
3993 | 83 char pot_etags_version[] = "@(#) pot revision number is 17.33"; |
428 | 84 |
85 #define TRUE 1 | |
86 #define FALSE 0 | |
87 | |
458 | 88 #ifdef DEBUG |
89 # undef DEBUG | |
90 # define DEBUG TRUE | |
91 #else | |
92 # define DEBUG FALSE | |
93 # define NDEBUG /* disable assert */ | |
428 | 94 #endif |
95 | |
96 #ifdef HAVE_CONFIG_H | |
97 # include <config.h> | |
98 /* On some systems, Emacs defines static as nothing for the sake | |
99 of unexec. We don't want that here since we don't use unexec. */ | |
100 # undef static | |
3524 | 101 # ifndef PTR /* for XEmacs */ |
2225 | 102 # define PTR void * |
715 | 103 # endif |
3524 | 104 # ifndef __P /* for XEmacs */ |
2225 | 105 # define __P(args) args |
709 | 106 # endif |
2325 | 107 #else /* no config.h */ |
709 | 108 # if defined(__STDC__) && (__STDC__ || defined(__SUNPRO_C)) |
109 # define __P(args) args /* use prototypes */ | |
110 # define PTR void * /* for generic pointers */ | |
2325 | 111 # else /* not standard C */ |
709 | 112 # define __P(args) () /* no prototypes */ |
113 # define const /* remove const for old compilers' sake */ | |
114 # define PTR long * /* don't use void* */ | |
531 | 115 # endif |
116 #endif /* !HAVE_CONFIG_H */ | |
428 | 117 |
432 | 118 #ifndef _GNU_SOURCE |
119 # define _GNU_SOURCE 1 /* enables some compiler checks on GNU */ | |
120 #endif | |
121 | |
3524 | 122 /* WIN32_NATIVE is for XEmacs. |
458 | 123 MSDOS, WINDOWSNT, DOS_NT are for Emacs. */ |
442 | 124 #ifdef WIN32_NATIVE |
458 | 125 # undef MSDOS |
126 # undef WINDOWSNT | |
127 # define WINDOWSNT | |
128 #endif /* WIN32_NATIVE */ | |
129 | |
130 #ifdef MSDOS | |
131 # undef MSDOS | |
132 # define MSDOS TRUE | |
133 # include <fcntl.h> | |
134 # include <sys/param.h> | |
135 # include <io.h> | |
136 # ifndef HAVE_CONFIG_H | |
137 # define DOS_NT | |
138 # include <sys/config.h> | |
139 # endif | |
140 #else | |
141 # define MSDOS FALSE | |
142 #endif /* MSDOS */ | |
143 | |
144 #ifdef WINDOWSNT | |
428 | 145 # include <stdlib.h> |
146 # include <fcntl.h> | |
147 # include <string.h> | |
442 | 148 # include <direct.h> |
428 | 149 # include <io.h> |
150 # define MAXPATHLEN _MAX_PATH | |
458 | 151 # undef HAVE_NTGUI |
152 # undef DOS_NT | |
153 # define DOS_NT | |
428 | 154 # ifndef HAVE_GETCWD |
155 # define HAVE_GETCWD | |
156 # endif /* undef HAVE_GETCWD */ | |
2325 | 157 #else /* not WINDOWSNT */ |
442 | 158 # ifdef STDC_HEADERS |
159 # include <stdlib.h> | |
160 # include <string.h> | |
2325 | 161 # else /* no standard C headers */ |
3972 | 162 extern char *getenv (); |
163 extern char *strcpy (); | |
164 extern char *strncpy (); | |
165 extern char *strcat (); | |
166 extern char *strncat (); | |
167 extern unsigned long strlen (); | |
168 extern PTR malloc (); | |
169 extern PTR realloc (); | |
2325 | 170 # ifdef VMS |
171 # define EXIT_SUCCESS 1 | |
172 # define EXIT_FAILURE 0 | |
173 # else /* no VMS */ | |
174 # define EXIT_SUCCESS 0 | |
175 # define EXIT_FAILURE 1 | |
176 # endif | |
442 | 177 # endif |
458 | 178 #endif /* !WINDOWSNT */ |
428 | 179 |
180 #ifdef HAVE_UNISTD_H | |
181 # include <unistd.h> | |
182 #else | |
458 | 183 # if defined (HAVE_GETCWD) && !defined (WINDOWSNT) |
442 | 184 extern char *getcwd (char *buf, size_t size); |
428 | 185 # endif |
186 #endif /* HAVE_UNISTD_H */ | |
187 | |
188 #include <stdio.h> | |
189 #include <ctype.h> | |
190 #include <errno.h> | |
442 | 191 #ifndef errno |
192 extern int errno; | |
193 #endif | |
428 | 194 #include <sys/types.h> |
195 #include <sys/stat.h> | |
196 | |
458 | 197 #include <assert.h> |
198 #ifdef NDEBUG | |
199 # undef assert /* some systems have a buggy assert.h */ | |
200 # define assert(x) ((void) 0) | |
201 #endif | |
202 | |
428 | 203 #if !defined (S_ISREG) && defined (S_IFREG) |
204 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) | |
205 #endif | |
206 | |
3517 | 207 #ifdef NO_LONG_OPTIONS /* define this if you don't have GNU getopt */ |
208 # define NO_LONG_OPTIONS TRUE | |
428 | 209 # define getopt_long(argc,argv,optstr,lopts,lind) getopt (argc, argv, optstr) |
210 extern char *optarg; | |
211 extern int optind, opterr; | |
3517 | 212 #else |
213 # define NO_LONG_OPTIONS FALSE | |
214 # include <getopt.h> | |
215 #endif /* NO_LONG_OPTIONS */ | |
216 | |
217 #ifndef HAVE_CONFIG_H /* this is a standalone compilation */ | |
218 # ifdef __CYGWIN__ /* compiling on Cygwin */ | |
709 | 219 !!! NOTICE !!! |
220 the regex.h distributed with Cygwin is not compatible with etags, alas! | |
221 If you want regular expression support, you should delete this notice and | |
222 arrange to use the GNU regex.h and regex.c. | |
223 # endif | |
3517 | 224 #endif |
225 #include <regex.h> | |
428 | 226 |
227 /* Define CTAGS to make the program "ctags" compatible with the usual one. | |
228 Leave it undefined to make the program "etags", which makes emacs-style | |
229 tag tables and tags typedefs, #defines and struct/union/enum by default. */ | |
230 #ifdef CTAGS | |
231 # undef CTAGS | |
232 # define CTAGS TRUE | |
233 #else | |
234 # define CTAGS FALSE | |
235 #endif | |
236 | |
458 | 237 #define streq(s,t) (assert((s)!=NULL || (t)!=NULL), !strcmp (s, t)) |
2225 | 238 #define strcaseeq(s,t) (assert((s)!=NULL && (t)!=NULL), !etags_strcasecmp (s, t)) |
458 | 239 #define strneq(s,t,n) (assert((s)!=NULL || (t)!=NULL), !strncmp (s, t, n)) |
2225 | 240 #define strncaseeq(s,t,n) (assert((s)!=NULL && (t)!=NULL), !etags_strncasecmp (s, t, n)) |
428 | 241 |
242 #define CHARS 256 /* 2^sizeof(char) */ | |
458 | 243 #define CHAR(x) ((unsigned int)(x) & (CHARS - 1)) |
2225 | 244 #define iswhite(c) (_wht[CHAR(c)]) /* c is white (see white) */ |
245 #define notinname(c) (_nin[CHAR(c)]) /* c is not in a name (see nonam) */ | |
246 #define begtoken(c) (_btk[CHAR(c)]) /* c can start token (see begtk) */ | |
247 #define intoken(c) (_itk[CHAR(c)]) /* c can be in token (see midtk) */ | |
248 #define endtoken(c) (_etk[CHAR(c)]) /* c ends tokens (see endtk) */ | |
428 | 249 |
458 | 250 #define ISALNUM(c) isalnum (CHAR(c)) |
251 #define ISALPHA(c) isalpha (CHAR(c)) | |
252 #define ISDIGIT(c) isdigit (CHAR(c)) | |
253 #define ISLOWER(c) islower (CHAR(c)) | |
254 | |
255 #define lowcase(c) tolower (CHAR(c)) | |
256 #define upcase(c) toupper (CHAR(c)) | |
257 | |
428 | 258 |
259 /* | |
260 * xnew, xrnew -- allocate, reallocate storage | |
261 * | |
262 * SYNOPSIS: Type *xnew (int n, Type); | |
458 | 263 * void xrnew (OldPointer, int n, Type); |
428 | 264 */ |
458 | 265 #if DEBUG |
428 | 266 # include "chkmalloc.h" |
267 # define xnew(n,Type) ((Type *) trace_malloc (__FILE__, __LINE__, \ | |
268 (n) * sizeof (Type))) | |
458 | 269 # define xrnew(op,n,Type) ((op) = (Type *) trace_realloc (__FILE__, __LINE__, \ |
270 (char *) (op), (n) * sizeof (Type))) | |
428 | 271 #else |
272 # define xnew(n,Type) ((Type *) xmalloc ((n) * sizeof (Type))) | |
458 | 273 # define xrnew(op,n,Type) ((op) = (Type *) xrealloc ( \ |
274 (char *) (op), (n) * sizeof (Type))) | |
428 | 275 #endif |
276 | |
709 | 277 #define bool int |
278 | |
279 typedef void Lang_function __P((FILE *)); | |
428 | 280 |
281 typedef struct | |
282 { | |
2225 | 283 char *suffix; /* file name suffix for this compressor */ |
284 char *command; /* takes one arg and decompresses to stdout */ | |
428 | 285 } compressor; |
286 | |
287 typedef struct | |
288 { | |
2225 | 289 char *name; /* language name */ |
290 char *help; /* detailed help for the language */ | |
291 Lang_function *function; /* parse function */ | |
292 char **suffixes; /* name suffixes of this language's files */ | |
293 char **filenames; /* names of this language's files */ | |
294 char **interpreters; /* interpreters for this language */ | |
295 bool metasource; /* source used to generate other sources */ | |
428 | 296 } language; |
297 | |
2225 | 298 typedef struct fdesc |
299 { | |
300 struct fdesc *next; /* for the linked list */ | |
301 char *infname; /* uncompressed input file name */ | |
302 char *infabsname; /* absolute uncompressed input file name */ | |
303 char *infabsdir; /* absolute dir of input file */ | |
304 char *taggedfname; /* file name to write in tagfile */ | |
305 language *lang; /* language of file */ | |
306 char *prop; /* file properties to write in tagfile */ | |
307 bool usecharno; /* etags tags shall contain char number */ | |
308 bool written; /* entry written in the tags file */ | |
309 } fdesc; | |
310 | |
428 | 311 typedef struct node_st |
2225 | 312 { /* sorting structure */ |
313 struct node_st *left, *right; /* left and right sons */ | |
314 fdesc *fdp; /* description of file to whom tag belongs */ | |
315 char *name; /* tag name */ | |
316 char *regex; /* search regexp */ | |
317 bool valid; /* write this tag on the tag file */ | |
318 bool is_func; /* function tag: use regexp in CTAGS mode */ | |
319 bool been_warned; /* warning already given for duplicated tag */ | |
320 int lno; /* line number tag is on */ | |
428 | 321 long cno; /* character number line starts on */ |
322 } node; | |
323 | |
324 /* | |
325 * A `linebuffer' is a structure which holds a line of text. | |
326 * `readline_internal' reads a line from a stream into a linebuffer | |
327 * and works regardless of the length of the line. | |
328 * SIZE is the size of BUFFER, LEN is the length of the string in | |
329 * BUFFER after readline reads it. | |
330 */ | |
331 typedef struct | |
332 { | |
333 long size; | |
334 int len; | |
335 char *buffer; | |
336 } linebuffer; | |
337 | |
2225 | 338 /* Used to support mixing of --lang and file names. */ |
339 typedef struct | |
340 { | |
341 enum { | |
342 at_language, /* a language specification */ | |
343 at_regexp, /* a regular expression */ | |
344 at_filename, /* a file name */ | |
345 at_stdin, /* read from stdin here */ | |
346 at_end /* stop parsing the list */ | |
347 } arg_type; /* argument type */ | |
348 language *lang; /* language associated with the argument */ | |
349 char *what; /* the argument itself */ | |
350 } argument; | |
351 | |
352 /* Structure defining a regular expression. */ | |
353 typedef struct regexp | |
354 { | |
355 struct regexp *p_next; /* pointer to next in list */ | |
356 language *lang; /* if set, use only for this language */ | |
357 char *pattern; /* the regexp pattern */ | |
358 char *name; /* tag name */ | |
359 struct re_pattern_buffer *pat; /* the compiled pattern */ | |
360 struct re_registers regs; /* re registers */ | |
361 bool error_signaled; /* already signaled for this regexp */ | |
362 bool force_explicit_name; /* do not allow implict tag name */ | |
363 bool ignore_case; /* ignore case when matching */ | |
364 bool multi_line; /* do a multi-line match on the whole file */ | |
365 } regexp; | |
366 | |
367 | |
428 | 368 /* Many compilers barf on this: |
369 Lang_function Ada_funcs; | |
370 so let's write it this way */ | |
709 | 371 static void Ada_funcs __P((FILE *)); |
372 static void Asm_labels __P((FILE *)); | |
373 static void C_entries __P((int c_ext, FILE *)); | |
374 static void default_C_entries __P((FILE *)); | |
375 static void plain_C_entries __P((FILE *)); | |
376 static void Cjava_entries __P((FILE *)); | |
377 static void Cobol_paragraphs __P((FILE *)); | |
378 static void Cplusplus_entries __P((FILE *)); | |
379 static void Cstar_entries __P((FILE *)); | |
380 static void Erlang_functions __P((FILE *)); | |
2554 | 381 static void Forth_words __P((FILE *)); |
709 | 382 static void Fortran_functions __P((FILE *)); |
2225 | 383 static void HTML_labels __P((FILE *)); |
709 | 384 static void Lisp_functions __P((FILE *)); |
2325 | 385 static void Lua_functions __P((FILE *)); |
709 | 386 static void Makefile_targets __P((FILE *)); |
387 static void Pascal_functions __P((FILE *)); | |
388 static void Perl_functions __P((FILE *)); | |
389 static void PHP_functions __P((FILE *)); | |
2225 | 390 static void PS_functions __P((FILE *)); |
709 | 391 static void Prolog_functions __P((FILE *)); |
392 static void Python_functions __P((FILE *)); | |
393 static void Scheme_functions __P((FILE *)); | |
394 static void TeX_commands __P((FILE *)); | |
395 static void Texinfo_nodes __P((FILE *)); | |
2225 | 396 static void Yacc_entries __P((FILE *)); |
709 | 397 static void just_read_file __P((FILE *)); |
398 | |
399 static void print_language_names __P((void)); | |
400 static void print_version __P((void)); | |
2225 | 401 static void print_help __P((argument *)); |
709 | 402 int main __P((int, char **)); |
403 | |
404 static compressor *get_compressor_from_suffix __P((char *, char **)); | |
405 static language *get_language_from_langname __P((const char *)); | |
406 static language *get_language_from_interpreter __P((char *)); | |
2225 | 407 static language *get_language_from_filename __P((char *, bool)); |
408 static void readline __P((linebuffer *, FILE *)); | |
709 | 409 static long readline_internal __P((linebuffer *, FILE *)); |
2225 | 410 static bool nocase_tail __P((char *)); |
411 static void get_tag __P((char *, char **)); | |
428 | 412 |
2225 | 413 static void analyse_regex __P((char *)); |
414 static void free_regexps __P((void)); | |
415 static void regex_tag_multiline __P((void)); | |
709 | 416 static void error __P((const char *, const char *)); |
417 static void suggest_asking_for_help __P((void)); | |
418 void fatal __P((char *, char *)); | |
419 static void pfatal __P((char *)); | |
420 static void add_node __P((node *, node **)); | |
421 | |
422 static void init __P((void)); | |
2225 | 423 static void process_file_name __P((char *, language *)); |
424 static void process_file __P((FILE *, char *, language *)); | |
425 static void find_entries __P((FILE *)); | |
709 | 426 static void free_tree __P((node *)); |
2225 | 427 static void free_fdesc __P((fdesc *)); |
709 | 428 static void pfnote __P((char *, bool, char *, int, int, long)); |
2225 | 429 static void make_tag __P((char *, int, bool, char *, int, int, long)); |
430 static void invalidate_nodes __P((fdesc *, node **)); | |
709 | 431 static void put_entries __P((node *)); |
432 | |
433 static char *concat __P((char *, char *, char *)); | |
434 static char *skip_spaces __P((char *)); | |
435 static char *skip_non_spaces __P((char *)); | |
436 static char *savenstr __P((char *, int)); | |
437 static char *savestr __P((char *)); | |
438 static char *etags_strchr __P((const char *, int)); | |
439 static char *etags_strrchr __P((const char *, int)); | |
2225 | 440 static int etags_strcasecmp __P((const char *, const char *)); |
441 static int etags_strncasecmp __P((const char *, const char *, int)); | |
709 | 442 static char *etags_getcwd __P((void)); |
443 static char *relative_filename __P((char *, char *)); | |
444 static char *absolute_filename __P((char *, char *)); | |
445 static char *absolute_dirname __P((char *, char *)); | |
446 static bool filename_is_absolute __P((char *f)); | |
447 static void canonicalize_filename __P((char *)); | |
2225 | 448 static void linebuffer_init __P((linebuffer *)); |
709 | 449 static void linebuffer_setlen __P((linebuffer *, int)); |
2225 | 450 static PTR xmalloc __P((unsigned int)); |
451 static PTR xrealloc __P((char *, unsigned int)); | |
428 | 452 |
453 | |
2225 | 454 static char searchar = '/'; /* use /.../ searches */ |
455 | |
456 static char *tagfile; /* output file */ | |
457 static char *progname; /* name this program was invoked with */ | |
458 static char *cwd; /* current working directory */ | |
459 static char *tagfiledir; /* directory of tagfile */ | |
460 static FILE *tagf; /* ioptr for tags file */ | |
461 | |
462 static fdesc *fdhead; /* head of file description list */ | |
463 static fdesc *curfdp; /* current file description */ | |
464 static int lineno; /* line number of current line */ | |
465 static long charno; /* current character number */ | |
466 static long linecharno; /* charno of start of current line */ | |
467 static char *dbp; /* pointer to start of current tag */ | |
468 | |
469 static const int invalidcharno = -1; | |
470 | |
471 static node *nodehead; /* the head of the binary tree of tags */ | |
472 static node *last_node; /* the last node created */ | |
473 | |
474 static linebuffer lb; /* the current line */ | |
475 static linebuffer filebuf; /* a buffer containing the whole file */ | |
476 static linebuffer token_name; /* a buffer containing a tag name */ | |
428 | 477 |
478 /* boolean "functions" (see init) */ | |
2225 | 479 static bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS]; |
480 static char | |
428 | 481 /* white chars */ |
442 | 482 *white = " \f\t\n\r\v", |
428 | 483 /* not in a name */ |
2225 | 484 *nonam = " \f\t\n\r()=,;", /* look at make_tag before modifying! */ |
428 | 485 /* token ending chars */ |
486 *endtk = " \t\n\r\"'#()[]{}=-+%*/&|^~!<>;,.:?", | |
487 /* token starting chars */ | |
488 *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~@", | |
489 /* valid in-token chars */ | |
490 *midtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789"; | |
491 | |
2225 | 492 static bool append_to_tagfile; /* -a: append to tags */ |
3972 | 493 /* The next five default to TRUE for etags, but to FALSE for ctags. */ |
2225 | 494 static bool typedefs; /* -t: create tags for C and Ada typedefs */ |
495 static bool typedefs_or_cplusplus; /* -T: create tags for C typedefs, level */ | |
428 | 496 /* 0 struct/enum/union decls, and C++ */ |
497 /* member functions. */ | |
2225 | 498 static bool constantypedefs; /* -d: create tags for C #define, enum */ |
428 | 499 /* constants and variables. */ |
500 /* -D: opposite of -d. Default under ctags. */ | |
2225 | 501 static bool globals; /* create tags for global variables */ |
3972 | 502 static bool members; /* create tags for C member variables */ |
2225 | 503 static bool declarations; /* --declarations: tag them and extern in C&Co*/ |
504 static bool no_line_directive; /* ignore #line directives (undocumented) */ | |
3876 | 505 static bool no_duplicates; /* no duplicate tags for ctags (undocumented) */ |
2225 | 506 static bool update; /* -u: update tags */ |
507 static bool vgrind_style; /* -v: create vgrind style index output */ | |
3876 | 508 static bool no_warnings; /* -w: suppress warnings (undocumented) */ |
2225 | 509 static bool cxref_style; /* -x: create cxref style output */ |
510 static bool cplusplus; /* .[hc] means C++, not C */ | |
511 static bool ignoreindent; /* -I: ignore indentation in C */ | |
512 static bool packages_only; /* --packages-only: in Ada, only tag packages*/ | |
513 | |
3517 | 514 /* STDIN is defined in LynxOS system headers */ |
515 #ifdef STDIN | |
516 # undef STDIN | |
517 #endif | |
518 | |
2225 | 519 #define STDIN 0x1001 /* returned by getopt_long on --parse-stdin */ |
520 static bool parsing_stdin; /* --parse-stdin used */ | |
521 | |
522 static regexp *p_head; /* list of all regexps */ | |
523 static bool need_filebuf; /* some regexes are multi-line */ | |
3517 | 524 |
2225 | 525 static struct option longopts[] = |
428 | 526 { |
3876 | 527 { "append", no_argument, NULL, 'a' }, |
528 { "packages-only", no_argument, &packages_only, TRUE }, | |
529 { "c++", no_argument, NULL, 'C' }, | |
530 { "declarations", no_argument, &declarations, TRUE }, | |
531 { "no-line-directive", no_argument, &no_line_directive, TRUE }, | |
532 { "no-duplicates", no_argument, &no_duplicates, TRUE }, | |
533 { "help", no_argument, NULL, 'h' }, | |
534 { "help", no_argument, NULL, 'H' }, | |
535 { "ignore-indentation", no_argument, NULL, 'I' }, | |
536 { "language", required_argument, NULL, 'l' }, | |
537 { "members", no_argument, &members, TRUE }, | |
538 { "no-members", no_argument, &members, FALSE }, | |
539 { "output", required_argument, NULL, 'o' }, | |
540 { "regex", required_argument, NULL, 'r' }, | |
541 { "no-regex", no_argument, NULL, 'R' }, | |
542 { "ignore-case-regex", required_argument, NULL, 'c' }, | |
2225 | 543 { "parse-stdin", required_argument, NULL, STDIN }, |
3876 | 544 { "version", no_argument, NULL, 'V' }, |
2225 | 545 |
3090 | 546 #if CTAGS /* Ctags options */ |
3876 | 547 { "backward-search", no_argument, NULL, 'B' }, |
548 { "cxref", no_argument, NULL, 'x' }, | |
549 { "defines", no_argument, NULL, 'd' }, | |
550 { "globals", no_argument, &globals, TRUE }, | |
551 { "typedefs", no_argument, NULL, 't' }, | |
552 { "typedefs-and-c++", no_argument, NULL, 'T' }, | |
553 { "update", no_argument, NULL, 'u' }, | |
554 { "vgrind", no_argument, NULL, 'v' }, | |
555 { "no-warn", no_argument, NULL, 'w' }, | |
2225 | 556 |
3090 | 557 #else /* Etags options */ |
3876 | 558 { "no-defines", no_argument, NULL, 'D' }, |
559 { "no-globals", no_argument, &globals, FALSE }, | |
560 { "include", required_argument, NULL, 'i' }, | |
2225 | 561 #endif |
428 | 562 { NULL } |
563 }; | |
564 | |
2225 | 565 static compressor compressors[] = |
428 | 566 { |
567 { "z", "gzip -d -c"}, | |
568 { "Z", "gzip -d -c"}, | |
569 { "gz", "gzip -d -c"}, | |
570 { "GZ", "gzip -d -c"}, | |
571 { "bz2", "bzip2 -d -c" }, | |
572 { NULL } | |
573 }; | |
574 | |
575 /* | |
576 * Language stuff. | |
577 */ | |
578 | |
579 /* Ada code */ | |
2225 | 580 static char *Ada_suffixes [] = |
428 | 581 { "ads", "adb", "ada", NULL }; |
2225 | 582 static char Ada_help [] = |
583 "In Ada code, functions, procedures, packages, tasks and types are\n\ | |
584 tags. Use the `--packages-only' option to create tags for\n\ | |
585 packages only.\n\ | |
586 Ada tag names have suffixes indicating the type of entity:\n\ | |
587 Entity type: Qualifier:\n\ | |
588 ------------ ----------\n\ | |
589 function /f\n\ | |
590 procedure /p\n\ | |
591 package spec /s\n\ | |
592 package body /b\n\ | |
593 type /t\n\ | |
594 task /k\n\ | |
595 Thus, `M-x find-tag <RET> bidule/b <RET>' will go directly to the\n\ | |
596 body of the package `bidule', while `M-x find-tag <RET> bidule <RET>'\n\ | |
597 will just search for any tag `bidule'."; | |
428 | 598 |
599 /* Assembly code */ | |
2225 | 600 static char *Asm_suffixes [] = |
601 { "a", /* Unix assembler */ | |
602 "asm", /* Microcontroller assembly */ | |
603 "def", /* BSO/Tasking definition includes */ | |
604 "inc", /* Microcontroller include files */ | |
605 "ins", /* Microcontroller include files */ | |
606 "s", "sa", /* Unix assembler */ | |
607 "S", /* cpp-processed Unix assembler */ | |
608 "src", /* BSO/Tasking C compiler output */ | |
609 NULL | |
610 }; | |
611 static char Asm_help [] = | |
612 "In assembler code, labels appearing at the beginning of a line,\n\ | |
613 followed by a colon, are tags."; | |
614 | |
428 | 615 |
616 /* Note that .c and .h can be considered C++, if the --c++ flag was | |
2225 | 617 given, or if the `class' or `template' keyowrds are met inside the file. |
458 | 618 That is why default_C_entries is called for these. */ |
2225 | 619 static char *default_C_suffixes [] = |
428 | 620 { "c", "h", NULL }; |
2225 | 621 static char default_C_help [] = |
622 "In C code, any C function or typedef is a tag, and so are\n\ | |
623 definitions of `struct', `union' and `enum'. `#define' macro\n\ | |
624 definitions and `enum' constants are tags unless you specify\n\ | |
625 `--no-defines'. Global variables are tags unless you specify\n\ | |
3972 | 626 `--no-globals' and so are struct members unless you specify\n\ |
627 `--no-members'. Use of `--no-globals', `--no-defines' and\n\ | |
628 `--no-members' can make the tags table file much smaller.\n\ | |
2225 | 629 You can tag function declarations and external variables by\n\ |
3972 | 630 using `--declarations'."; |
2225 | 631 |
632 static char *Cplusplus_suffixes [] = | |
458 | 633 { "C", "c++", "cc", "cpp", "cxx", "H", "h++", "hh", "hpp", "hxx", |
428 | 634 "M", /* Objective C++ */ |
635 "pdb", /* Postscript with C syntax */ | |
636 NULL }; | |
2225 | 637 static char Cplusplus_help [] = |
638 "In C++ code, all the tag constructs of C code are tagged. (Use\n\ | |
639 --help --lang=c --lang=c++ for full help.)\n\ | |
3972 | 640 In addition to C tags, member functions are also recognized. Member\n\ |
641 variables are recognized unless you use the `--no-members' option.\n\ | |
2225 | 642 Tags for variables and functions in classes are named `CLASS::VARIABLE'\n\ |
643 and `CLASS::FUNCTION'. `operator' definitions have tag names like\n\ | |
644 `operator+'."; | |
645 | |
646 static char *Cjava_suffixes [] = | |
428 | 647 { "java", NULL }; |
2225 | 648 static char Cjava_help [] = |
649 "In Java code, all the tags constructs of C and C++ code are\n\ | |
650 tagged. (Use --help --lang=c --lang=c++ --lang=java for full help.)"; | |
651 | |
652 | |
653 static char *Cobol_suffixes [] = | |
428 | 654 { "COB", "cob", NULL }; |
2225 | 655 static char Cobol_help [] = |
656 "In Cobol code, tags are paragraph names; that is, any word\n\ | |
657 starting in column 8 and followed by a period."; | |
658 | |
659 static char *Cstar_suffixes [] = | |
428 | 660 { "cs", "hs", NULL }; |
661 | |
2225 | 662 static char *Erlang_suffixes [] = |
428 | 663 { "erl", "hrl", NULL }; |
2225 | 664 static char Erlang_help [] = |
665 "In Erlang code, the tags are the functions, records and macros\n\ | |
666 defined in the file."; | |
667 | |
2554 | 668 char *Forth_suffixes [] = |
669 { "fth", "tok", NULL }; | |
670 static char Forth_help [] = | |
671 "In Forth code, tags are words defined by `:',\n\ | |
672 constant, code, create, defer, value, variable, buffer:, field."; | |
673 | |
2225 | 674 static char *Fortran_suffixes [] = |
428 | 675 { "F", "f", "f90", "for", NULL }; |
2225 | 676 static char Fortran_help [] = |
677 "In Fortran code, functions, subroutines and block data are tags."; | |
678 | |
679 static char *HTML_suffixes [] = | |
680 { "htm", "html", "shtml", NULL }; | |
681 static char HTML_help [] = | |
682 "In HTML input files, the tags are the `title' and the `h1', `h2',\n\ | |
683 `h3' headers. Also, tags are `name=' in anchors and all\n\ | |
684 occurrences of `id='."; | |
685 | |
686 static char *Lisp_suffixes [] = | |
458 | 687 { "cl", "clisp", "el", "l", "lisp", "LSP", "lsp", "ml", NULL }; |
2225 | 688 static char Lisp_help [] = |
689 "In Lisp code, any function defined with `defun', any variable\n\ | |
690 defined with `defvar' or `defconst', and in general the first\n\ | |
691 argument of any expression that starts with `(def' in column zero\n\ | |
692 is a tag."; | |
693 | |
2325 | 694 static char *Lua_suffixes [] = |
695 { "lua", "LUA", NULL }; | |
696 static char Lua_help [] = | |
697 "In Lua scripts, all functions are tags."; | |
698 | |
2225 | 699 static char *Makefile_filenames [] = |
458 | 700 { "Makefile", "makefile", "GNUMakefile", "Makefile.in", "Makefile.am", NULL}; |
2225 | 701 static char Makefile_help [] = |
702 "In makefiles, targets are tags; additionally, variables are tags\n\ | |
703 unless you specify `--no-globals'."; | |
704 | |
705 static char *Objc_suffixes [] = | |
458 | 706 { "lm", /* Objective lex file */ |
428 | 707 "m", /* Objective C file */ |
708 NULL }; | |
2225 | 709 static char Objc_help [] = |
710 "In Objective C code, tags include Objective C definitions for classes,\n\ | |
711 class categories, methods and protocols. Tags for variables and\n\ | |
3517 | 712 functions in classes are named `CLASS::VARIABLE' and `CLASS::FUNCTION'.\n\ |
713 (Use --help --lang=c --lang=objc --lang=java for full help.)"; | |
2225 | 714 |
715 static char *Pascal_suffixes [] = | |
716 { "p", "pas", NULL }; | |
717 static char Pascal_help [] = | |
718 "In Pascal code, the tags are the functions and procedures defined\n\ | |
719 in the file."; | |
3517 | 720 /* " // this is for working around an Emacs highlighting bug... */ |
2225 | 721 |
722 static char *Perl_suffixes [] = | |
723 { "pl", "pm", NULL }; | |
724 static char *Perl_interpreters [] = | |
725 { "perl", "@PERL@", NULL }; | |
726 static char Perl_help [] = | |
727 "In Perl code, the tags are the packages, subroutines and variables\n\ | |
728 defined by the `package', `sub', `my' and `local' keywords. Use\n\ | |
729 `--globals' if you want to tag global variables. Tags for\n\ | |
730 subroutines are named `PACKAGE::SUB'. The name for subroutines\n\ | |
731 defined in the default package is `main::SUB'."; | |
732 | |
733 static char *PHP_suffixes [] = | |
734 { "php", "php3", "php4", NULL }; | |
735 static char PHP_help [] = | |
3972 | 736 "In PHP code, tags are functions, classes and defines. Unless you use\n\ |
737 the `--no-members' option, vars are tags too."; | |
2225 | 738 |
739 static char *plain_C_suffixes [] = | |
740 { "pc", /* Pro*C file */ | |
741 NULL }; | |
742 | |
743 static char *PS_suffixes [] = | |
428 | 744 { "ps", "psw", NULL }; /* .psw is for PSWrap */ |
2225 | 745 static char PS_help [] = |
746 "In PostScript code, the tags are the functions."; | |
747 | |
748 static char *Prolog_suffixes [] = | |
428 | 749 { "prolog", NULL }; |
2225 | 750 static char Prolog_help [] = |
751 "In Prolog code, tags are predicates and rules at the beginning of\n\ | |
752 line."; | |
753 | |
754 static char *Python_suffixes [] = | |
428 | 755 { "py", NULL }; |
2225 | 756 static char Python_help [] = |
757 "In Python code, `def' or `class' at the beginning of a line\n\ | |
758 generate a tag."; | |
428 | 759 |
760 /* Can't do the `SCM' or `scm' prefix with a version number. */ | |
2225 | 761 static char *Scheme_suffixes [] = |
458 | 762 { "oak", "sch", "scheme", "SCM", "scm", "SM", "sm", "ss", "t", NULL }; |
2225 | 763 static char Scheme_help [] = |
764 "In Scheme code, tags include anything defined with `def' or with a\n\ | |
765 construct whose name starts with `def'. They also include\n\ | |
766 variables set with `set!' at top level in the file."; | |
767 | |
768 static char *TeX_suffixes [] = | |
458 | 769 { "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL }; |
2225 | 770 static char TeX_help [] = |
771 "In LaTeX text, the argument of any of the commands `\\chapter',\n\ | |
772 `\\section', `\\subsection', `\\subsubsection', `\\eqno', `\\label',\n\ | |
773 `\\ref', `\\cite', `\\bibitem', `\\part', `\\appendix', `\\entry',\n\ | |
774 `\\index', `\\def', `\\newcommand', `\\renewcommand',\n\ | |
775 `\\newenvironment' or `\\renewenvironment' is a tag.\n\ | |
776 \n\ | |
777 Other commands can be specified by setting the environment variable\n\ | |
778 `TEXTAGS' to a colon-separated list like, for example,\n\ | |
779 TEXTAGS=\"mycommand:myothercommand\"."; | |
780 | |
781 | |
782 static char *Texinfo_suffixes [] = | |
458 | 783 { "texi", "texinfo", "txi", NULL }; |
2225 | 784 static char Texinfo_help [] = |
785 "for texinfo files, lines starting with @node are tagged."; | |
786 | |
787 static char *Yacc_suffixes [] = | |
458 | 788 { "y", "y++", "ym", "yxx", "yy", NULL }; /* .ym is Objective yacc file */ |
2225 | 789 static char Yacc_help [] = |
790 "In Bison or Yacc input files, each rule defines as a tag the\n\ | |
791 nonterminal it constructs. The portions of the file that contain\n\ | |
792 C code are parsed as C code (use --help --lang=c --lang=yacc\n\ | |
793 for full help)."; | |
794 | |
795 static char auto_help [] = | |
796 "`auto' is not a real language, it indicates to use\n\ | |
797 a default language for files base on file name suffix and file contents."; | |
798 | |
799 static char none_help [] = | |
800 "`none' is not a real language, it indicates to only do\n\ | |
801 regexp processing on files."; | |
802 | |
803 static char no_lang_help [] = | |
804 "No detailed help available for this language."; | |
805 | |
428 | 806 |
807 /* | |
808 * Table of languages. | |
809 * | |
810 * It is ok for a given function to be listed under more than one | |
811 * name. I just didn't. | |
812 */ | |
813 | |
2225 | 814 static language lang_names [] = |
428 | 815 { |
2225 | 816 { "ada", Ada_help, Ada_funcs, Ada_suffixes }, |
817 { "asm", Asm_help, Asm_labels, Asm_suffixes }, | |
818 { "c", default_C_help, default_C_entries, default_C_suffixes }, | |
819 { "c++", Cplusplus_help, Cplusplus_entries, Cplusplus_suffixes }, | |
820 { "c*", no_lang_help, Cstar_entries, Cstar_suffixes }, | |
821 { "cobol", Cobol_help, Cobol_paragraphs, Cobol_suffixes }, | |
822 { "erlang", Erlang_help, Erlang_functions, Erlang_suffixes }, | |
2554 | 823 { "forth", Forth_help, Forth_words, Forth_suffixes }, |
2225 | 824 { "fortran", Fortran_help, Fortran_functions, Fortran_suffixes }, |
825 { "html", HTML_help, HTML_labels, HTML_suffixes }, | |
826 { "java", Cjava_help, Cjava_entries, Cjava_suffixes }, | |
827 { "lisp", Lisp_help, Lisp_functions, Lisp_suffixes }, | |
2325 | 828 { "lua", Lua_help, Lua_functions, Lua_suffixes }, |
2225 | 829 { "makefile", Makefile_help,Makefile_targets,NULL,Makefile_filenames}, |
830 { "objc", Objc_help, plain_C_entries, Objc_suffixes }, | |
831 { "pascal", Pascal_help, Pascal_functions, Pascal_suffixes }, | |
832 { "perl",Perl_help,Perl_functions,Perl_suffixes,NULL,Perl_interpreters}, | |
833 { "php", PHP_help, PHP_functions, PHP_suffixes }, | |
834 { "postscript",PS_help, PS_functions, PS_suffixes }, | |
835 { "proc", no_lang_help, plain_C_entries, plain_C_suffixes }, | |
836 { "prolog", Prolog_help, Prolog_functions, Prolog_suffixes }, | |
837 { "python", Python_help, Python_functions, Python_suffixes }, | |
838 { "scheme", Scheme_help, Scheme_functions, Scheme_suffixes }, | |
839 { "tex", TeX_help, TeX_commands, TeX_suffixes }, | |
840 { "texinfo", Texinfo_help, Texinfo_nodes, Texinfo_suffixes }, | |
841 { "yacc", Yacc_help,Yacc_entries,Yacc_suffixes,NULL,NULL,TRUE}, | |
842 { "auto", auto_help }, /* default guessing scheme */ | |
843 { "none", none_help, just_read_file }, /* regexp matching only */ | |
844 { NULL } /* end of list */ | |
428 | 845 }; |
458 | 846 |
428 | 847 |
848 static void | |
849 print_language_names () | |
850 { | |
851 language *lang; | |
458 | 852 char **name, **ext; |
428 | 853 |
854 puts ("\nThese are the currently supported languages, along with the\n\ | |
458 | 855 default file names and dot suffixes:"); |
428 | 856 for (lang = lang_names; lang->name != NULL; lang++) |
857 { | |
458 | 858 printf (" %-*s", 10, lang->name); |
859 if (lang->filenames != NULL) | |
860 for (name = lang->filenames; *name != NULL; name++) | |
861 printf (" %s", *name); | |
428 | 862 if (lang->suffixes != NULL) |
863 for (ext = lang->suffixes; *ext != NULL; ext++) | |
864 printf (" .%s", *ext); | |
865 puts (""); | |
866 } | |
2225 | 867 puts ("where `auto' means use default language for files based on file\n\ |
428 | 868 name suffix, and `none' means only do regexp processing on files.\n\ |
869 If no language is specified and no matching suffix is found,\n\ | |
870 the first line of the file is read for a sharp-bang (#!) sequence\n\ | |
871 followed by the name of an interpreter. If no such sequence is found,\n\ | |
872 Fortran is tried first; if no tags are found, C is tried next.\n\ | |
2225 | 873 When parsing any C file, a \"class\" or \"template\" keyword\n\ |
874 switches to C++."); | |
875 puts ("Compressed files are supported using gzip and bzip2.\n\ | |
876 \n\ | |
877 For detailed help on a given language use, for example,\n\ | |
878 etags --help --lang=ada."); | |
428 | 879 } |
880 | |
442 | 881 #ifndef EMACS_NAME |
2225 | 882 # define EMACS_NAME "standalone" |
428 | 883 #endif |
3972 | 884 #ifndef VERSION |
3993 | 885 # define VERSION "17.33" |
428 | 886 #endif |
887 static void | |
888 print_version () | |
889 { | |
3972 | 890 printf ("%s (%s %s)\n", (CTAGS) ? "ctags" : "etags", EMACS_NAME, VERSION); |
891 puts ("Copyright (C) 2007 Free Software Foundation, Inc."); | |
892 puts ("This program is distributed under the terms in ETAGS.README"); | |
428 | 893 |
2225 | 894 exit (EXIT_SUCCESS); |
428 | 895 } |
896 | |
3993 | 897 #ifndef PRINT_UNDOCUMENTED_OPTIONS_HELP |
898 # define PRINT_UNDOCUMENTED_OPTIONS_HELP FALSE | |
899 #endif | |
900 | |
428 | 901 static void |
2225 | 902 print_help (argbuffer) |
903 argument *argbuffer; | |
428 | 904 { |
2225 | 905 bool help_for_lang = FALSE; |
906 | |
907 for (; argbuffer->arg_type != at_end; argbuffer++) | |
908 if (argbuffer->arg_type == at_language) | |
909 { | |
910 if (help_for_lang) | |
911 puts (""); | |
912 puts (argbuffer->lang->help); | |
913 help_for_lang = TRUE; | |
914 } | |
915 | |
916 if (help_for_lang) | |
917 exit (EXIT_SUCCESS); | |
918 | |
428 | 919 printf ("Usage: %s [options] [[regex-option ...] file-name] ...\n\ |
920 \n\ | |
921 These are the options accepted by %s.\n", progname, progname); | |
3517 | 922 if (NO_LONG_OPTIONS) |
923 puts ("WARNING: long option names do not work with this executable,\n\ | |
924 as it is not linked with GNU getopt."); | |
2325 | 925 else |
3517 | 926 puts ("You may use unambiguous abbreviations for the long option names."); |
2225 | 927 puts (" A - as file name means read names from stdin (one per line).\n\ |
928 Absolute names are stored in the output file as they are.\n\ | |
929 Relative ones are stored relative to the output file's directory.\n"); | |
930 | |
3090 | 931 puts ("-a, --append\n\ |
428 | 932 Append tag entries to existing tags file."); |
933 | |
934 puts ("--packages-only\n\ | |
2225 | 935 For Ada files, only generate tags for packages."); |
428 | 936 |
937 if (CTAGS) | |
938 puts ("-B, --backward-search\n\ | |
939 Write the search commands for the tag entries using '?', the\n\ | |
940 backward-search command instead of '/', the forward-search command."); | |
941 | |
458 | 942 /* This option is mostly obsolete, because etags can now automatically |
943 detect C++. Retained for backward compatibility and for debugging and | |
944 experimentation. In principle, we could want to tag as C++ even | |
2225 | 945 before any "class" or "template" keyword. |
428 | 946 puts ("-C, --c++\n\ |
947 Treat files whose name suffix defaults to C language as C++ files."); | |
458 | 948 */ |
428 | 949 |
950 puts ("--declarations\n\ | |
951 In C and derived languages, create tags for function declarations,"); | |
952 if (CTAGS) | |
953 puts ("\tand create tags for extern variables if --globals is used."); | |
954 else | |
955 puts | |
956 ("\tand create tags for extern variables unless --no-globals is used."); | |
957 | |
958 if (CTAGS) | |
959 puts ("-d, --defines\n\ | |
960 Create tag entries for C #define constants and enum constants, too."); | |
961 else | |
962 puts ("-D, --no-defines\n\ | |
963 Don't create tag entries for C #define constants and enum constants.\n\ | |
964 This makes the tags file smaller."); | |
965 | |
966 if (!CTAGS) | |
2225 | 967 puts ("-i FILE, --include=FILE\n\ |
428 | 968 Include a note in tag file indicating that, when searching for\n\ |
969 a tag, one should also consult the tags file FILE after\n\ | |
970 checking the current file."); | |
2225 | 971 |
972 puts ("-l LANG, --language=LANG\n\ | |
428 | 973 Force the following files to be considered as written in the\n\ |
974 named language up to the next --language=LANG option."); | |
975 | |
976 if (CTAGS) | |
977 puts ("--globals\n\ | |
978 Create tag entries for global variables in some languages."); | |
979 else | |
980 puts ("--no-globals\n\ | |
981 Do not create tag entries for global variables in some\n\ | |
982 languages. This makes the tags file smaller."); | |
3993 | 983 |
984 if (PRINT_UNDOCUMENTED_OPTIONS_HELP) | |
985 puts ("--no-line-directive\n\ | |
986 Ignore #line preprocessor directives in C and derived languages."); | |
987 | |
3972 | 988 if (CTAGS) |
989 puts ("--members\n\ | |
2225 | 990 Create tag entries for members of structures in some languages."); |
3972 | 991 else |
992 puts ("--no-members\n\ | |
993 Do not create tag entries for members of structures\n\ | |
994 in some languages."); | |
428 | 995 |
2225 | 996 puts ("-r REGEXP, --regex=REGEXP or --regex=@regexfile\n\ |
997 Make a tag for each line matching a regular expression pattern\n\ | |
998 in the following files. {LANGUAGE}REGEXP uses REGEXP for LANGUAGE\n\ | |
999 files only. REGEXFILE is a file containing one REGEXP per line.\n\ | |
1000 REGEXP takes the form /TAGREGEXP/TAGNAME/MODS, where TAGNAME/ is\n\ | |
1001 optional. The TAGREGEXP pattern is anchored (as if preceded by ^)."); | |
1002 puts (" If TAGNAME/ is present, the tags created are named.\n\ | |
428 | 1003 For example Tcl named tags can be created with:\n\ |
2225 | 1004 --regex=\"/proc[ \\t]+\\([^ \\t]+\\)/\\1/.\".\n\ |
1005 MODS are optional one-letter modifiers: `i' means to ignore case,\n\ | |
1006 `m' means to allow multi-line matches, `s' implies `m' and\n\ | |
1007 causes dot to match any character, including newline."); | |
3993 | 1008 |
428 | 1009 puts ("-R, --no-regex\n\ |
1010 Don't create tags from regexps for the following files."); | |
3993 | 1011 |
2225 | 1012 puts ("-I, --ignore-indentation\n\ |
1013 In C and C++ do not assume that a closing brace in the first\n\ | |
1014 column is the final brace of a function or structure definition."); | |
3993 | 1015 |
428 | 1016 puts ("-o FILE, --output=FILE\n\ |
1017 Write the tags to FILE."); | |
3993 | 1018 |
2225 | 1019 puts ("--parse-stdin=NAME\n\ |
1020 Read from standard input and record tags as belonging to file NAME."); | |
428 | 1021 |
1022 if (CTAGS) | |
1023 { | |
1024 puts ("-t, --typedefs\n\ | |
1025 Generate tag entries for C and Ada typedefs."); | |
1026 puts ("-T, --typedefs-and-c++\n\ | |
1027 Generate tag entries for C typedefs, C struct/enum/union tags,\n\ | |
1028 and C++ member functions."); | |
2225 | 1029 } |
1030 | |
1031 if (CTAGS) | |
1032 puts ("-u, --update\n\ | |
428 | 1033 Update the tag entries for the given files, leaving tag\n\ |
1034 entries for other files in place. Currently, this is\n\ | |
1035 implemented by deleting the existing entries for the given\n\ | |
1036 files and then rewriting the new entries at the end of the\n\ | |
1037 tags file. It is often faster to simply rebuild the entire\n\ | |
1038 tag file than to use this."); | |
2225 | 1039 |
1040 if (CTAGS) | |
1041 { | |
428 | 1042 puts ("-v, --vgrind\n\ |
3090 | 1043 Print on the standard output an index of items intended for\n\ |
1044 human consumption, similar to the output of vgrind. The index\n\ | |
1045 is sorted, and gives the page number of each item."); | |
3993 | 1046 |
1047 if (PRINT_UNDOCUMENTED_OPTIONS_HELP) | |
1048 puts ("-w, --no-duplicates\n\ | |
3876 | 1049 Do not create duplicate tag entries, for compatibility with\n\ |
1050 traditional ctags."); | |
3993 | 1051 |
1052 if (PRINT_UNDOCUMENTED_OPTIONS_HELP) | |
1053 puts ("-w, --no-warn\n\ | |
3876 | 1054 Suppress warning messages about duplicate tag entries."); |
3993 | 1055 |
428 | 1056 puts ("-x, --cxref\n\ |
1057 Like --vgrind, but in the style of cxref, rather than vgrind.\n\ | |
1058 The output uses line numbers instead of page numbers, but\n\ | |
1059 beyond that the differences are cosmetic; try both to see\n\ | |
1060 which you like."); | |
1061 } | |
1062 | |
1063 puts ("-V, --version\n\ | |
1064 Print the version of the program.\n\ | |
1065 -h, --help\n\ | |
2225 | 1066 Print this help message.\n\ |
1067 Followed by one or more `--language' options prints detailed\n\ | |
1068 help about tag generation for the specified languages."); | |
428 | 1069 |
1070 print_language_names (); | |
1071 | |
1072 puts (""); | |
1073 puts ("Report bugs to bug-gnu-emacs@gnu.org"); | |
1074 | |
2225 | 1075 exit (EXIT_SUCCESS); |
428 | 1076 } |
1077 | |
1078 | |
1079 #ifdef VMS /* VMS specific functions */ | |
1080 | |
1081 #define EOS '\0' | |
1082 | |
1083 /* This is a BUG! ANY arbitrary limit is a BUG! | |
1084 Won't someone please fix this? */ | |
1085 #define MAX_FILE_SPEC_LEN 255 | |
1086 typedef struct { | |
1087 short curlen; | |
1088 char body[MAX_FILE_SPEC_LEN + 1]; | |
1089 } vspec; | |
1090 | |
1091 /* | |
1092 v1.05 nmm 26-Jun-86 fn_exp - expand specification of list of file names | |
1093 returning in each successive call the next file name matching the input | |
1094 spec. The function expects that each in_spec passed | |
1095 to it will be processed to completion; in particular, up to and | |
1096 including the call following that in which the last matching name | |
1097 is returned, the function ignores the value of in_spec, and will | |
1098 only start processing a new spec with the following call. | |
1099 If an error occurs, on return out_spec contains the value | |
1100 of in_spec when the error occurred. | |
1101 | |
1102 With each successive file name returned in out_spec, the | |
1103 function's return value is one. When there are no more matching | |
1104 names the function returns zero. If on the first call no file | |
1105 matches in_spec, or there is any other error, -1 is returned. | |
1106 */ | |
1107 | |
1108 #include <rmsdef.h> | |
1109 #include <descrip.h> | |
1110 #define OUTSIZE MAX_FILE_SPEC_LEN | |
442 | 1111 static short |
428 | 1112 fn_exp (out, in) |
1113 vspec *out; | |
1114 char *in; | |
1115 { | |
1116 static long context = 0; | |
1117 static struct dsc$descriptor_s o; | |
1118 static struct dsc$descriptor_s i; | |
1119 static bool pass1 = TRUE; | |
1120 long status; | |
1121 short retval; | |
1122 | |
1123 if (pass1) | |
1124 { | |
1125 pass1 = FALSE; | |
1126 o.dsc$a_pointer = (char *) out; | |
1127 o.dsc$w_length = (short)OUTSIZE; | |
1128 i.dsc$a_pointer = in; | |
1129 i.dsc$w_length = (short)strlen(in); | |
1130 i.dsc$b_dtype = DSC$K_DTYPE_T; | |
1131 i.dsc$b_class = DSC$K_CLASS_S; | |
1132 o.dsc$b_dtype = DSC$K_DTYPE_VT; | |
1133 o.dsc$b_class = DSC$K_CLASS_VS; | |
1134 } | |
1135 if ((status = lib$find_file(&i, &o, &context, 0, 0)) == RMS$_NORMAL) | |
1136 { | |
1137 out->body[out->curlen] = EOS; | |
1138 return 1; | |
1139 } | |
1140 else if (status == RMS$_NMF) | |
1141 retval = 0; | |
1142 else | |
1143 { | |
1144 strcpy(out->body, in); | |
1145 retval = -1; | |
1146 } | |
1147 lib$find_file_end(&context); | |
1148 pass1 = TRUE; | |
1149 return retval; | |
1150 } | |
1151 | |
1152 /* | |
1153 v1.01 nmm 19-Aug-85 gfnames - return in successive calls the | |
1154 name of each file specified by the provided arg expanding wildcards. | |
1155 */ | |
442 | 1156 static char * |
428 | 1157 gfnames (arg, p_error) |
1158 char *arg; | |
1159 bool *p_error; | |
1160 { | |
1161 static vspec filename = {MAX_FILE_SPEC_LEN, "\0"}; | |
1162 | |
1163 switch (fn_exp (&filename, arg)) | |
1164 { | |
1165 case 1: | |
1166 *p_error = FALSE; | |
1167 return filename.body; | |
1168 case 0: | |
1169 *p_error = FALSE; | |
1170 return NULL; | |
1171 default: | |
1172 *p_error = TRUE; | |
1173 return filename.body; | |
1174 } | |
1175 } | |
1176 | |
1177 #ifndef OLD /* Newer versions of VMS do provide `system'. */ | |
1178 system (cmd) | |
1179 char *cmd; | |
1180 { | |
1181 error ("%s", "system() function not implemented under VMS"); | |
1182 } | |
1183 #endif | |
1184 | |
1185 #define VERSION_DELIM ';' | |
1186 char *massage_name (s) | |
1187 char *s; | |
1188 { | |
1189 char *start = s; | |
1190 | |
1191 for ( ; *s; s++) | |
1192 if (*s == VERSION_DELIM) | |
1193 { | |
1194 *s = EOS; | |
1195 break; | |
1196 } | |
1197 else | |
1198 *s = lowcase (*s); | |
1199 return start; | |
1200 } | |
1201 #endif /* VMS */ | |
1202 | |
1203 | |
1204 int | |
1205 main (argc, argv) | |
1206 int argc; | |
1207 char *argv[]; | |
1208 { | |
1209 int i; | |
1210 unsigned int nincluded_files; | |
1211 char **included_files; | |
1212 argument *argbuffer; | |
1213 int current_arg, file_count; | |
1214 linebuffer filename_lb; | |
2225 | 1215 bool help_asked = FALSE; |
428 | 1216 #ifdef VMS |
1217 bool got_err; | |
1218 #endif | |
2225 | 1219 char *optstring; |
1220 int opt; | |
1221 | |
428 | 1222 |
458 | 1223 #ifdef DOS_NT |
428 | 1224 _fmode = O_BINARY; /* all of files are treated as binary files */ |
458 | 1225 #endif /* DOS_NT */ |
428 | 1226 |
1227 progname = argv[0]; | |
1228 nincluded_files = 0; | |
1229 included_files = xnew (argc, char *); | |
1230 current_arg = 0; | |
1231 file_count = 0; | |
1232 | |
1233 /* Allocate enough no matter what happens. Overkill, but each one | |
1234 is small. */ | |
1235 argbuffer = xnew (argc, argument); | |
1236 | |
1237 /* | |
1238 * If etags, always find typedefs and structure tags. Why not? | |
3972 | 1239 * Also default to find macro constants, enum constants, struct |
1240 * members and global variables. | |
428 | 1241 */ |
1242 if (!CTAGS) | |
1243 { | |
458 | 1244 typedefs = typedefs_or_cplusplus = constantypedefs = TRUE; |
3972 | 1245 globals = members = TRUE; |
428 | 1246 } |
1247 | |
2554 | 1248 /* When the optstring begins with a '-' getopt_long does not rearrange the |
1249 non-options arguments to be at the end, but leaves them alone. */ | |
3517 | 1250 optstring = concat (NO_LONG_OPTIONS ? "" : "-", |
1251 "ac:Cf:Il:o:r:RSVhH", | |
3090 | 1252 (CTAGS) ? "BxdtTuvw" : "Di:"); |
1253 | |
1254 while ((opt = getopt_long (argc, argv, optstring, longopts, NULL)) != EOF) | |
2225 | 1255 switch (opt) |
1256 { | |
1257 case 0: | |
1258 /* If getopt returns 0, then it has already processed a | |
1259 long-named option. We should do nothing. */ | |
428 | 1260 break; |
1261 | |
2225 | 1262 case 1: |
1263 /* This means that a file name has been seen. Record it. */ | |
1264 argbuffer[current_arg].arg_type = at_filename; | |
1265 argbuffer[current_arg].what = optarg; | |
1266 ++current_arg; | |
1267 ++file_count; | |
1268 break; | |
1269 | |
1270 case STDIN: | |
1271 /* Parse standard input. Idea by Vivek <vivek@etla.org>. */ | |
1272 argbuffer[current_arg].arg_type = at_stdin; | |
1273 argbuffer[current_arg].what = optarg; | |
1274 ++current_arg; | |
1275 ++file_count; | |
1276 if (parsing_stdin) | |
1277 fatal ("cannot parse standard input more than once", (char *)NULL); | |
1278 parsing_stdin = TRUE; | |
1279 break; | |
1280 | |
1281 /* Common options. */ | |
3090 | 1282 case 'a': append_to_tagfile = TRUE; break; |
2225 | 1283 case 'C': cplusplus = TRUE; break; |
1284 case 'f': /* for compatibility with old makefiles */ | |
1285 case 'o': | |
1286 if (tagfile) | |
428 | 1287 { |
2225 | 1288 error ("-o option may only be given once.", (char *)NULL); |
1289 suggest_asking_for_help (); | |
1290 /* NOTREACHED */ | |
428 | 1291 } |
2225 | 1292 tagfile = optarg; |
1293 break; | |
1294 case 'I': | |
1295 case 'S': /* for backward compatibility */ | |
1296 ignoreindent = TRUE; | |
1297 break; | |
1298 case 'l': | |
1299 { | |
1300 language *lang = get_language_from_langname (optarg); | |
1301 if (lang != NULL) | |
1302 { | |
1303 argbuffer[current_arg].lang = lang; | |
1304 argbuffer[current_arg].arg_type = at_language; | |
1305 ++current_arg; | |
1306 } | |
428 | 1307 } |
2225 | 1308 break; |
1309 case 'c': | |
1310 /* Backward compatibility: support obsolete --ignore-case-regexp. */ | |
1311 optarg = concat (optarg, "i", ""); /* memory leak here */ | |
1312 /* FALLTHRU */ | |
1313 case 'r': | |
1314 argbuffer[current_arg].arg_type = at_regexp; | |
1315 argbuffer[current_arg].what = optarg; | |
1316 ++current_arg; | |
1317 break; | |
1318 case 'R': | |
1319 argbuffer[current_arg].arg_type = at_regexp; | |
1320 argbuffer[current_arg].what = NULL; | |
1321 ++current_arg; | |
1322 break; | |
1323 case 'V': | |
1324 print_version (); | |
1325 break; | |
1326 case 'h': | |
1327 case 'H': | |
1328 help_asked = TRUE; | |
1329 break; | |
1330 | |
1331 /* Etags options */ | |
1332 case 'D': constantypedefs = FALSE; break; | |
1333 case 'i': included_files[nincluded_files++] = optarg; break; | |
1334 | |
1335 /* Ctags options. */ | |
1336 case 'B': searchar = '?'; break; | |
1337 case 'd': constantypedefs = TRUE; break; | |
1338 case 't': typedefs = TRUE; break; | |
1339 case 'T': typedefs = typedefs_or_cplusplus = TRUE; break; | |
1340 case 'u': update = TRUE; break; | |
1341 case 'v': vgrind_style = TRUE; /*FALLTHRU*/ | |
1342 case 'x': cxref_style = TRUE; break; | |
1343 case 'w': no_warnings = TRUE; break; | |
1344 default: | |
1345 suggest_asking_for_help (); | |
1346 /* NOTREACHED */ | |
1347 } | |
1348 | |
2554 | 1349 /* No more options. Store the rest of arguments. */ |
2225 | 1350 for (; optind < argc; optind++) |
428 | 1351 { |
1352 argbuffer[current_arg].arg_type = at_filename; | |
1353 argbuffer[current_arg].what = argv[optind]; | |
1354 ++current_arg; | |
1355 ++file_count; | |
1356 } | |
1357 | |
2225 | 1358 argbuffer[current_arg].arg_type = at_end; |
1359 | |
1360 if (help_asked) | |
1361 print_help (argbuffer); | |
1362 /* NOTREACHED */ | |
1363 | |
428 | 1364 if (nincluded_files == 0 && file_count == 0) |
1365 { | |
1366 error ("no input files specified.", (char *)NULL); | |
1367 suggest_asking_for_help (); | |
2225 | 1368 /* NOTREACHED */ |
428 | 1369 } |
1370 | |
1371 if (tagfile == NULL) | |
1372 tagfile = CTAGS ? "tags" : "TAGS"; | |
1373 cwd = etags_getcwd (); /* the current working directory */ | |
1374 if (cwd[strlen (cwd) - 1] != '/') | |
1375 { | |
1376 char *oldcwd = cwd; | |
1377 cwd = concat (oldcwd, "/", ""); | |
1378 free (oldcwd); | |
1379 } | |
2325 | 1380 /* Relative file names are made relative to the current directory. */ |
1381 if (streq (tagfile, "-") | |
1382 || strneq (tagfile, "/dev/", 5)) | |
428 | 1383 tagfiledir = cwd; |
1384 else | |
1385 tagfiledir = absolute_dirname (tagfile, cwd); | |
1386 | |
1387 init (); /* set up boolean "functions" */ | |
1388 | |
2225 | 1389 linebuffer_init (&lb); |
1390 linebuffer_init (&filename_lb); | |
1391 linebuffer_init (&filebuf); | |
1392 linebuffer_init (&token_name); | |
428 | 1393 |
1394 if (!CTAGS) | |
1395 { | |
1396 if (streq (tagfile, "-")) | |
1397 { | |
1398 tagf = stdout; | |
458 | 1399 #ifdef DOS_NT |
428 | 1400 /* Switch redirected `stdout' to binary mode (setting `_fmode' |
1401 doesn't take effect until after `stdout' is already open). */ | |
1402 if (!isatty (fileno (stdout))) | |
1403 setmode (fileno (stdout), O_BINARY); | |
458 | 1404 #endif /* DOS_NT */ |
428 | 1405 } |
1406 else | |
1407 tagf = fopen (tagfile, append_to_tagfile ? "a" : "w"); | |
1408 if (tagf == NULL) | |
1409 pfatal (tagfile); | |
1410 } | |
1411 | |
1412 /* | |
1413 * Loop through files finding functions. | |
1414 */ | |
2225 | 1415 for (i = 0; i < current_arg; i++) |
428 | 1416 { |
2225 | 1417 static language *lang; /* non-NULL if language is forced */ |
1418 char *this_file; | |
1419 | |
428 | 1420 switch (argbuffer[i].arg_type) |
1421 { | |
1422 case at_language: | |
2225 | 1423 lang = argbuffer[i].lang; |
428 | 1424 break; |
1425 case at_regexp: | |
2225 | 1426 analyse_regex (argbuffer[i].what); |
428 | 1427 break; |
1428 case at_filename: | |
1429 #ifdef VMS | |
1430 while ((this_file = gfnames (argbuffer[i].what, &got_err)) != NULL) | |
1431 { | |
1432 if (got_err) | |
1433 { | |
1434 error ("can't find file %s\n", this_file); | |
1435 argc--, argv++; | |
1436 } | |
1437 else | |
1438 { | |
1439 this_file = massage_name (this_file); | |
1440 } | |
1441 #else | |
1442 this_file = argbuffer[i].what; | |
1443 #endif | |
1444 /* Input file named "-" means read file names from stdin | |
1445 (one per line) and use them. */ | |
1446 if (streq (this_file, "-")) | |
2225 | 1447 { |
1448 if (parsing_stdin) | |
1449 fatal ("cannot parse standard input AND read file names from it", | |
1450 (char *)NULL); | |
1451 while (readline_internal (&filename_lb, stdin) > 0) | |
1452 process_file_name (filename_lb.buffer, lang); | |
1453 } | |
428 | 1454 else |
2225 | 1455 process_file_name (this_file, lang); |
428 | 1456 #ifdef VMS |
1457 } | |
1458 #endif | |
1459 break; | |
2225 | 1460 case at_stdin: |
1461 this_file = argbuffer[i].what; | |
1462 process_file (stdin, this_file, lang); | |
1463 break; | |
428 | 1464 } |
1465 } | |
1466 | |
2225 | 1467 free_regexps (); |
1468 free (lb.buffer); | |
1469 free (filebuf.buffer); | |
1470 free (token_name.buffer); | |
1471 | |
1472 if (!CTAGS || cxref_style) | |
428 | 1473 { |
3090 | 1474 /* Write the remaining tags to tagf (ETAGS) or stdout (CXREF). */ |
1475 put_entries (nodehead); | |
2225 | 1476 free_tree (nodehead); |
1477 nodehead = NULL; | |
1478 if (!CTAGS) | |
1479 { | |
1480 fdesc *fdp; | |
1481 | |
1482 /* Output file entries that have no tags. */ | |
1483 for (fdp = fdhead; fdp != NULL; fdp = fdp->next) | |
1484 if (!fdp->written) | |
1485 fprintf (tagf, "\f\n%s,0\n", fdp->taggedfname); | |
1486 | |
1487 while (nincluded_files-- > 0) | |
1488 fprintf (tagf, "\f\n%s,include\n", *included_files++); | |
3090 | 1489 |
1490 if (fclose (tagf) == EOF) | |
1491 pfatal (tagfile); | |
2225 | 1492 } |
1493 | |
1494 exit (EXIT_SUCCESS); | |
428 | 1495 } |
1496 | |
1497 if (update) | |
1498 { | |
1499 char cmd[BUFSIZ]; | |
1500 for (i = 0; i < current_arg; ++i) | |
1501 { | |
2225 | 1502 switch (argbuffer[i].arg_type) |
1503 { | |
1504 case at_filename: | |
1505 case at_stdin: | |
1506 break; | |
1507 default: | |
1508 continue; /* the for loop */ | |
1509 } | |
428 | 1510 sprintf (cmd, |
1511 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS", | |
1512 tagfile, argbuffer[i].what, tagfile); | |
2225 | 1513 if (system (cmd) != EXIT_SUCCESS) |
428 | 1514 fatal ("failed to execute shell command", (char *)NULL); |
1515 } | |
1516 append_to_tagfile = TRUE; | |
1517 } | |
1518 | |
1519 tagf = fopen (tagfile, append_to_tagfile ? "a" : "w"); | |
1520 if (tagf == NULL) | |
1521 pfatal (tagfile); | |
2225 | 1522 put_entries (nodehead); /* write all the tags (CTAGS) */ |
1523 free_tree (nodehead); | |
1524 nodehead = NULL; | |
1525 if (fclose (tagf) == EOF) | |
1526 pfatal (tagfile); | |
428 | 1527 |
3090 | 1528 if (CTAGS) |
1529 if (append_to_tagfile || update) | |
1530 { | |
3876 | 1531 char cmd[2*BUFSIZ+20]; |
1532 /* Maybe these should be used: | |
1533 setenv ("LC_COLLATE", "C", 1); | |
1534 setenv ("LC_ALL", "C", 1); */ | |
1535 sprintf (cmd, "sort -u -o %.*s %.*s", BUFSIZ, tagfile, BUFSIZ, tagfile); | |
3090 | 1536 exit (system (cmd)); |
1537 } | |
2225 | 1538 return EXIT_SUCCESS; |
428 | 1539 } |
1540 | |
1541 | |
1542 /* | |
1543 * Return a compressor given the file name. If EXTPTR is non-zero, | |
1544 * return a pointer into FILE where the compressor-specific | |
1545 * extension begins. If no compressor is found, NULL is returned | |
1546 * and EXTPTR is not significant. | |
458 | 1547 * Idea by Vladimir Alexiev <vladimir@cs.ualberta.ca> (1998) |
428 | 1548 */ |
442 | 1549 static compressor * |
428 | 1550 get_compressor_from_suffix (file, extptr) |
1551 char *file; | |
1552 char **extptr; | |
1553 { | |
1554 compressor *compr; | |
1555 char *slash, *suffix; | |
1556 | |
1557 /* This relies on FN to be after canonicalize_filename, | |
458 | 1558 so we don't need to consider backslashes on DOS_NT. */ |
428 | 1559 slash = etags_strrchr (file, '/'); |
1560 suffix = etags_strrchr (file, '.'); | |
1561 if (suffix == NULL || suffix < slash) | |
1562 return NULL; | |
1563 if (extptr != NULL) | |
1564 *extptr = suffix; | |
1565 suffix += 1; | |
1566 /* Let those poor souls who live with DOS 8+3 file name limits get | |
1567 some solace by treating foo.cgz as if it were foo.c.gz, etc. | |
458 | 1568 Only the first do loop is run if not MSDOS */ |
428 | 1569 do |
1570 { | |
1571 for (compr = compressors; compr->suffix != NULL; compr++) | |
1572 if (streq (compr->suffix, suffix)) | |
1573 return compr; | |
458 | 1574 if (!MSDOS) |
442 | 1575 break; /* do it only once: not really a loop */ |
428 | 1576 if (extptr != NULL) |
1577 *extptr = ++suffix; | |
1578 } while (*suffix != '\0'); | |
1579 return NULL; | |
1580 } | |
1581 | |
1582 | |
1583 | |
1584 /* | |
1585 * Return a language given the name. | |
1586 */ | |
442 | 1587 static language * |
458 | 1588 get_language_from_langname (name) |
709 | 1589 const char *name; |
428 | 1590 { |
1591 language *lang; | |
1592 | |
1593 if (name == NULL) | |
1594 error ("empty language name", (char *)NULL); | |
1595 else | |
1596 { | |
1597 for (lang = lang_names; lang->name != NULL; lang++) | |
1598 if (streq (name, lang->name)) | |
1599 return lang; | |
1600 error ("unknown language \"%s\"", name); | |
1601 } | |
1602 | |
1603 return NULL; | |
1604 } | |
1605 | |
1606 | |
1607 /* | |
1608 * Return a language given the interpreter name. | |
1609 */ | |
442 | 1610 static language * |
428 | 1611 get_language_from_interpreter (interpreter) |
1612 char *interpreter; | |
1613 { | |
1614 language *lang; | |
1615 char **iname; | |
1616 | |
1617 if (interpreter == NULL) | |
1618 return NULL; | |
1619 for (lang = lang_names; lang->name != NULL; lang++) | |
1620 if (lang->interpreters != NULL) | |
1621 for (iname = lang->interpreters; *iname != NULL; iname++) | |
1622 if (streq (*iname, interpreter)) | |
1623 return lang; | |
1624 | |
1625 return NULL; | |
1626 } | |
1627 | |
1628 | |
1629 | |
1630 /* | |
1631 * Return a language given the file name. | |
1632 */ | |
442 | 1633 static language * |
2225 | 1634 get_language_from_filename (file, case_sensitive) |
428 | 1635 char *file; |
2225 | 1636 bool case_sensitive; |
428 | 1637 { |
1638 language *lang; | |
458 | 1639 char **name, **ext, *suffix; |
1640 | |
1641 /* Try whole file name first. */ | |
1642 for (lang = lang_names; lang->name != NULL; lang++) | |
1643 if (lang->filenames != NULL) | |
1644 for (name = lang->filenames; *name != NULL; name++) | |
2225 | 1645 if ((case_sensitive) |
1646 ? streq (*name, file) | |
1647 : strcaseeq (*name, file)) | |
458 | 1648 return lang; |
1649 | |
1650 /* If not found, try suffix after last dot. */ | |
428 | 1651 suffix = etags_strrchr (file, '.'); |
1652 if (suffix == NULL) | |
1653 return NULL; | |
1654 suffix += 1; | |
1655 for (lang = lang_names; lang->name != NULL; lang++) | |
1656 if (lang->suffixes != NULL) | |
1657 for (ext = lang->suffixes; *ext != NULL; ext++) | |
2225 | 1658 if ((case_sensitive) |
1659 ? streq (*ext, suffix) | |
1660 : strcaseeq (*ext, suffix)) | |
428 | 1661 return lang; |
1662 return NULL; | |
1663 } | |
1664 | |
2225 | 1665 |
428 | 1666 /* |
1667 * This routine is called on each file argument. | |
1668 */ | |
442 | 1669 static void |
2225 | 1670 process_file_name (file, lang) |
428 | 1671 char *file; |
2225 | 1672 language *lang; |
428 | 1673 { |
1674 struct stat stat_buf; | |
1675 FILE *inf; | |
2225 | 1676 fdesc *fdp; |
428 | 1677 compressor *compr; |
1678 char *compressed_name, *uncompressed_name; | |
1679 char *ext, *real_name; | |
2225 | 1680 int retval; |
428 | 1681 |
1682 canonicalize_filename (file); | |
1683 if (streq (file, tagfile) && !streq (tagfile, "-")) | |
1684 { | |
1685 error ("skipping inclusion of %s in self.", file); | |
1686 return; | |
1687 } | |
1688 if ((compr = get_compressor_from_suffix (file, &ext)) == NULL) | |
1689 { | |
1690 compressed_name = NULL; | |
1691 real_name = uncompressed_name = savestr (file); | |
1692 } | |
1693 else | |
1694 { | |
1695 real_name = compressed_name = savestr (file); | |
1696 uncompressed_name = savenstr (file, ext - file); | |
1697 } | |
1698 | |
2225 | 1699 /* If the canonicalized uncompressed name |
1700 has already been dealt with, skip it silently. */ | |
1701 for (fdp = fdhead; fdp != NULL; fdp = fdp->next) | |
428 | 1702 { |
2225 | 1703 assert (fdp->infname != NULL); |
1704 if (streq (uncompressed_name, fdp->infname)) | |
1705 goto cleanup; | |
1706 } | |
428 | 1707 |
1708 if (stat (real_name, &stat_buf) != 0) | |
1709 { | |
1710 /* Reset real_name and try with a different name. */ | |
1711 real_name = NULL; | |
1712 if (compressed_name != NULL) /* try with the given suffix */ | |
1713 { | |
1714 if (stat (uncompressed_name, &stat_buf) == 0) | |
1715 real_name = uncompressed_name; | |
1716 } | |
1717 else /* try all possible suffixes */ | |
1718 { | |
1719 for (compr = compressors; compr->suffix != NULL; compr++) | |
1720 { | |
1721 compressed_name = concat (file, ".", compr->suffix); | |
1722 if (stat (compressed_name, &stat_buf) != 0) | |
1723 { | |
458 | 1724 if (MSDOS) |
1725 { | |
1726 char *suf = compressed_name + strlen (file); | |
1727 size_t suflen = strlen (compr->suffix) + 1; | |
1728 for ( ; suf[1]; suf++, suflen--) | |
1729 { | |
1730 memmove (suf, suf + 1, suflen); | |
1731 if (stat (compressed_name, &stat_buf) == 0) | |
1732 { | |
1733 real_name = compressed_name; | |
1734 break; | |
1735 } | |
1736 } | |
1737 if (real_name != NULL) | |
1738 break; | |
1739 } /* MSDOS */ | |
428 | 1740 free (compressed_name); |
1741 compressed_name = NULL; | |
1742 } | |
1743 else | |
1744 { | |
1745 real_name = compressed_name; | |
1746 break; | |
1747 } | |
1748 } | |
1749 } | |
1750 if (real_name == NULL) | |
1751 { | |
1752 perror (file); | |
2225 | 1753 goto cleanup; |
428 | 1754 } |
1755 } /* try with a different name */ | |
1756 | |
1757 if (!S_ISREG (stat_buf.st_mode)) | |
1758 { | |
1759 error ("skipping %s: it is not a regular file.", real_name); | |
2225 | 1760 goto cleanup; |
428 | 1761 } |
1762 if (real_name == compressed_name) | |
1763 { | |
1764 char *cmd = concat (compr->command, " ", real_name); | |
458 | 1765 inf = (FILE *) popen (cmd, "r"); |
428 | 1766 free (cmd); |
1767 } | |
1768 else | |
1769 inf = fopen (real_name, "r"); | |
1770 if (inf == NULL) | |
1771 { | |
1772 perror (real_name); | |
2225 | 1773 goto cleanup; |
428 | 1774 } |
1775 | |
2225 | 1776 process_file (inf, uncompressed_name, lang); |
428 | 1777 |
1778 if (real_name == compressed_name) | |
2225 | 1779 retval = pclose (inf); |
428 | 1780 else |
2225 | 1781 retval = fclose (inf); |
1782 if (retval < 0) | |
1783 pfatal (file); | |
1784 | |
1785 cleanup: | |
1786 if (compressed_name) free (compressed_name); | |
1787 if (uncompressed_name) free (uncompressed_name); | |
1788 last_node = NULL; | |
1789 curfdp = NULL; | |
1790 return; | |
1791 } | |
1792 | |
1793 static void | |
1794 process_file (fh, fn, lang) | |
1795 FILE *fh; | |
1796 char *fn; | |
1797 language *lang; | |
1798 { | |
1799 static const fdesc emptyfdesc; | |
1800 fdesc *fdp; | |
1801 | |
1802 /* Create a new input file description entry. */ | |
1803 fdp = xnew (1, fdesc); | |
1804 *fdp = emptyfdesc; | |
1805 fdp->next = fdhead; | |
1806 fdp->infname = savestr (fn); | |
1807 fdp->lang = lang; | |
1808 fdp->infabsname = absolute_filename (fn, cwd); | |
1809 fdp->infabsdir = absolute_dirname (fn, cwd); | |
1810 if (filename_is_absolute (fn)) | |
428 | 1811 { |
2225 | 1812 /* An absolute file name. Canonicalize it. */ |
1813 fdp->taggedfname = absolute_filename (fn, NULL); | |
1814 } | |
1815 else | |
1816 { | |
1817 /* A file name relative to cwd. Make it relative | |
1818 to the directory of the tags file. */ | |
1819 fdp->taggedfname = relative_filename (fn, tagfiledir); | |
1820 } | |
1821 fdp->usecharno = TRUE; /* use char position when making tags */ | |
1822 fdp->prop = NULL; | |
1823 fdp->written = FALSE; /* not written on tags file yet */ | |
1824 | |
1825 fdhead = fdp; | |
1826 curfdp = fdhead; /* the current file description */ | |
1827 | |
1828 find_entries (fh); | |
1829 | |
1830 /* If not Ctags, and if this is not metasource and if it contained no #line | |
1831 directives, we can write the tags and free all nodes pointing to | |
1832 curfdp. */ | |
1833 if (!CTAGS | |
1834 && curfdp->usecharno /* no #line directives in this file */ | |
1835 && !curfdp->lang->metasource) | |
1836 { | |
1837 node *np, *prev; | |
1838 | |
1839 /* Look for the head of the sublist relative to this file. See add_node | |
1840 for the structure of the node tree. */ | |
1841 prev = NULL; | |
1842 for (np = nodehead; np != NULL; prev = np, np = np->left) | |
1843 if (np->fdp == curfdp) | |
1844 break; | |
1845 | |
1846 /* If we generated tags for this file, write and delete them. */ | |
1847 if (np != NULL) | |
428 | 1848 { |
2225 | 1849 /* This is the head of the last sublist, if any. The following |
1850 instructions depend on this being true. */ | |
1851 assert (np->left == NULL); | |
1852 | |
1853 assert (fdhead == curfdp); | |
1854 assert (last_node->fdp == curfdp); | |
1855 put_entries (np); /* write tags for file curfdp->taggedfname */ | |
1856 free_tree (np); /* remove the written nodes */ | |
1857 if (prev == NULL) | |
1858 nodehead = NULL; /* no nodes left */ | |
1859 else | |
1860 prev->left = NULL; /* delete the pointer to the sublist */ | |
428 | 1861 } |
1862 } | |
1863 } | |
1864 | |
1865 /* | |
1866 * This routine sets up the boolean pseudo-functions which work | |
1867 * by setting boolean flags dependent upon the corresponding character. | |
1868 * Every char which is NOT in that string is not a white char. Therefore, | |
1869 * all of the array "_wht" is set to FALSE, and then the elements | |
1870 * subscripted by the chars in "white" are set to TRUE. Thus "_wht" | |
1871 * of a char is TRUE if it is the string "white", else FALSE. | |
1872 */ | |
442 | 1873 static void |
428 | 1874 init () |
1875 { | |
1876 register char *sp; | |
1877 register int i; | |
1878 | |
1879 for (i = 0; i < CHARS; i++) | |
1880 iswhite(i) = notinname(i) = begtoken(i) = intoken(i) = endtoken(i) = FALSE; | |
1881 for (sp = white; *sp != '\0'; sp++) iswhite (*sp) = TRUE; | |
1882 for (sp = nonam; *sp != '\0'; sp++) notinname (*sp) = TRUE; | |
442 | 1883 notinname('\0') = notinname('\n'); |
428 | 1884 for (sp = begtk; *sp != '\0'; sp++) begtoken (*sp) = TRUE; |
442 | 1885 begtoken('\0') = begtoken('\n'); |
428 | 1886 for (sp = midtk; *sp != '\0'; sp++) intoken (*sp) = TRUE; |
442 | 1887 intoken('\0') = intoken('\n'); |
428 | 1888 for (sp = endtk; *sp != '\0'; sp++) endtoken (*sp) = TRUE; |
1889 endtoken('\0') = endtoken('\n'); | |
1890 } | |
1891 | |
1892 /* | |
1893 * This routine opens the specified file and calls the function | |
1894 * which finds the function and type definitions. | |
1895 */ | |
442 | 1896 static void |
2225 | 1897 find_entries (inf) |
428 | 1898 FILE *inf; |
1899 { | |
1900 char *cp; | |
2225 | 1901 language *lang = curfdp->lang; |
1902 Lang_function *parser = NULL; | |
428 | 1903 |
1904 /* If user specified a language, use it. */ | |
1905 if (lang != NULL && lang->function != NULL) | |
1906 { | |
2225 | 1907 parser = lang->function; |
428 | 1908 } |
1909 | |
2225 | 1910 /* Else try to guess the language given the file name. */ |
1911 if (parser == NULL) | |
428 | 1912 { |
2225 | 1913 lang = get_language_from_filename (curfdp->infname, TRUE); |
1914 if (lang != NULL && lang->function != NULL) | |
1915 { | |
1916 curfdp->lang = lang; | |
1917 parser = lang->function; | |
1918 } | |
428 | 1919 } |
1920 | |
2225 | 1921 /* Else look for sharp-bang as the first two characters. */ |
1922 if (parser == NULL | |
1923 && readline_internal (&lb, inf) > 0 | |
428 | 1924 && lb.len >= 2 |
1925 && lb.buffer[0] == '#' | |
1926 && lb.buffer[1] == '!') | |
1927 { | |
1928 char *lp; | |
1929 | |
1930 /* Set lp to point at the first char after the last slash in the | |
1931 line or, if no slashes, at the first nonblank. Then set cp to | |
1932 the first successive blank and terminate the string. */ | |
1933 lp = etags_strrchr (lb.buffer+2, '/'); | |
1934 if (lp != NULL) | |
1935 lp += 1; | |
1936 else | |
1937 lp = skip_spaces (lb.buffer + 2); | |
1938 cp = skip_non_spaces (lp); | |
1939 *cp = '\0'; | |
1940 | |
1941 if (strlen (lp) > 0) | |
1942 { | |
1943 lang = get_language_from_interpreter (lp); | |
1944 if (lang != NULL && lang->function != NULL) | |
1945 { | |
2225 | 1946 curfdp->lang = lang; |
1947 parser = lang->function; | |
428 | 1948 } |
1949 } | |
1950 } | |
2225 | 1951 |
428 | 1952 /* We rewind here, even if inf may be a pipe. We fail if the |
1953 length of the first line is longer than the pipe block size, | |
1954 which is unlikely. */ | |
1955 rewind (inf); | |
1956 | |
2225 | 1957 /* Else try to guess the language given the case insensitive file name. */ |
1958 if (parser == NULL) | |
1959 { | |
1960 lang = get_language_from_filename (curfdp->infname, FALSE); | |
1961 if (lang != NULL && lang->function != NULL) | |
1962 { | |
1963 curfdp->lang = lang; | |
1964 parser = lang->function; | |
1965 } | |
1966 } | |
1967 | |
1968 /* Else try Fortran or C. */ | |
1969 if (parser == NULL) | |
428 | 1970 { |
2225 | 1971 node *old_last_node = last_node; |
1972 | |
1973 curfdp->lang = get_language_from_langname ("fortran"); | |
1974 find_entries (inf); | |
1975 | |
1976 if (old_last_node == last_node) | |
1977 /* No Fortran entries found. Try C. */ | |
1978 { | |
1979 /* We do not tag if rewind fails. | |
1980 Only the file name will be recorded in the tags file. */ | |
1981 rewind (inf); | |
1982 curfdp->lang = get_language_from_langname (cplusplus ? "c++" : "c"); | |
1983 find_entries (inf); | |
1984 } | |
1985 return; | |
428 | 1986 } |
2225 | 1987 |
1988 if (!no_line_directive | |
1989 && curfdp->lang != NULL && curfdp->lang->metasource) | |
1990 /* It may be that this is a bingo.y file, and we already parsed a bingo.c | |
1991 file, or anyway we parsed a file that is automatically generated from | |
1992 this one. If this is the case, the bingo.c file contained #line | |
1993 directives that generated tags pointing to this file. Let's delete | |
1994 them all before parsing this file, which is the real source. */ | |
1995 { | |
1996 fdesc **fdpp = &fdhead; | |
1997 while (*fdpp != NULL) | |
1998 if (*fdpp != curfdp | |
1999 && streq ((*fdpp)->taggedfname, curfdp->taggedfname)) | |
2000 /* We found one of those! We must delete both the file description | |
2001 and all tags referring to it. */ | |
2002 { | |
2003 fdesc *badfdp = *fdpp; | |
2004 | |
2005 /* Delete the tags referring to badfdp->taggedfname | |
2006 that were obtained from badfdp->infname. */ | |
2007 invalidate_nodes (badfdp, &nodehead); | |
2008 | |
2009 *fdpp = badfdp->next; /* remove the bad description from the list */ | |
2010 free_fdesc (badfdp); | |
2011 } | |
2012 else | |
2013 fdpp = &(*fdpp)->next; /* advance the list pointer */ | |
2014 } | |
2015 | |
2016 assert (parser != NULL); | |
2017 | |
2018 /* Generic initialisations before reading from file. */ | |
2019 linebuffer_setlen (&filebuf, 0); /* reset the file buffer */ | |
2020 | |
2021 /* Generic initialisations before parsing file with readline. */ | |
2022 lineno = 0; /* reset global line number */ | |
2023 charno = 0; /* reset global char number */ | |
2024 linecharno = 0; /* reset global char number of line start */ | |
2025 | |
2026 parser (inf); | |
2027 | |
2028 regex_tag_multiline (); | |
428 | 2029 } |
458 | 2030 |
428 | 2031 |
2225 | 2032 /* |
2033 * Check whether an implicitly named tag should be created, | |
2034 * then call `pfnote'. | |
2035 * NAME is a string that is internally copied by this function. | |
2036 * | |
2037 * TAGS format specification | |
2038 * Idea by Sam Kendall <kendall@mv.mv.com> (1997) | |
2039 * The following is explained in some more detail in etc/ETAGS.EBNF. | |
2040 * | |
2041 * make_tag creates tags with "implicit tag names" (unnamed tags) | |
2042 * if the following are all true, assuming NONAM=" \f\t\n\r()=,;": | |
2043 * 1. NAME does not contain any of the characters in NONAM; | |
2044 * 2. LINESTART contains name as either a rightmost, or rightmost but | |
2045 * one character, substring; | |
2046 * 3. the character, if any, immediately before NAME in LINESTART must | |
2047 * be a character in NONAM; | |
2048 * 4. the character, if any, immediately after NAME in LINESTART must | |
2049 * also be a character in NONAM. | |
2050 * | |
2051 * The implementation uses the notinname() macro, which recognises the | |
2052 * characters stored in the string `nonam'. | |
2053 * etags.el needs to use the same characters that are in NONAM. | |
2054 */ | |
2055 static void | |
2056 make_tag (name, namelen, is_func, linestart, linelen, lno, cno) | |
2057 char *name; /* tag name, or NULL if unnamed */ | |
2058 int namelen; /* tag length */ | |
2059 bool is_func; /* tag is a function */ | |
2060 char *linestart; /* start of the line where tag is */ | |
2061 int linelen; /* length of the line where tag is */ | |
2062 int lno; /* line number */ | |
2063 long cno; /* character number */ | |
2064 { | |
2065 bool named = (name != NULL && namelen > 0); | |
2066 | |
2067 if (!CTAGS && named) /* maybe set named to false */ | |
2068 /* Let's try to make an implicit tag name, that is, create an unnamed tag | |
2069 such that etags.el can guess a name from it. */ | |
2070 { | |
2071 int i; | |
2072 register char *cp = name; | |
2073 | |
2074 for (i = 0; i < namelen; i++) | |
2075 if (notinname (*cp++)) | |
2076 break; | |
2077 if (i == namelen) /* rule #1 */ | |
2078 { | |
2079 cp = linestart + linelen - namelen; | |
2080 if (notinname (linestart[linelen-1])) | |
2081 cp -= 1; /* rule #4 */ | |
2082 if (cp >= linestart /* rule #2 */ | |
2083 && (cp == linestart | |
2084 || notinname (cp[-1])) /* rule #3 */ | |
2085 && strneq (name, cp, namelen)) /* rule #2 */ | |
2086 named = FALSE; /* use implicit tag name */ | |
2087 } | |
2088 } | |
2089 | |
2090 if (named) | |
2091 name = savenstr (name, namelen); | |
2092 else | |
2093 name = NULL; | |
2094 pfnote (name, is_func, linestart, linelen, lno, cno); | |
2095 } | |
2096 | |
428 | 2097 /* Record a tag. */ |
442 | 2098 static void |
428 | 2099 pfnote (name, is_func, linestart, linelen, lno, cno) |
2100 char *name; /* tag name, or NULL if unnamed */ | |
2101 bool is_func; /* tag is a function */ | |
2102 char *linestart; /* start of the line where tag is */ | |
2103 int linelen; /* length of the line where tag is */ | |
2104 int lno; /* line number */ | |
2105 long cno; /* character number */ | |
2106 { | |
2107 register node *np; | |
2108 | |
2225 | 2109 assert (name == NULL || name[0] != '\0'); |
428 | 2110 if (CTAGS && name == NULL) |
2111 return; | |
2112 | |
2113 np = xnew (1, node); | |
2114 | |
2115 /* If ctags mode, change name "main" to M<thisfilename>. */ | |
2116 if (CTAGS && !cxref_style && streq (name, "main")) | |
2117 { | |
2225 | 2118 register char *fp = etags_strrchr (curfdp->taggedfname, '/'); |
2119 np->name = concat ("M", fp == NULL ? curfdp->taggedfname : fp + 1, ""); | |
428 | 2120 fp = etags_strrchr (np->name, '.'); |
2121 if (fp != NULL && fp[1] != '\0' && fp[2] == '\0') | |
2122 fp[0] = '\0'; | |
2123 } | |
2124 else | |
2125 np->name = name; | |
2225 | 2126 np->valid = TRUE; |
428 | 2127 np->been_warned = FALSE; |
2225 | 2128 np->fdp = curfdp; |
428 | 2129 np->is_func = is_func; |
2130 np->lno = lno; | |
2225 | 2131 if (np->fdp->usecharno) |
2132 /* Our char numbers are 0-base, because of C language tradition? | |
2133 ctags compatibility? old versions compatibility? I don't know. | |
2134 Anyway, since emacs's are 1-base we expect etags.el to take care | |
2135 of the difference. If we wanted to have 1-based numbers, we would | |
2136 uncomment the +1 below. */ | |
2137 np->cno = cno /* + 1 */ ; | |
2138 else | |
2139 np->cno = invalidcharno; | |
428 | 2140 np->left = np->right = NULL; |
2141 if (CTAGS && !cxref_style) | |
2142 { | |
2143 if (strlen (linestart) < 50) | |
2225 | 2144 np->regex = concat (linestart, "$", ""); |
428 | 2145 else |
2225 | 2146 np->regex = savenstr (linestart, 50); |
428 | 2147 } |
2148 else | |
2225 | 2149 np->regex = savenstr (linestart, linelen); |
2150 | |
2151 add_node (np, &nodehead); | |
428 | 2152 } |
2153 | |
2154 /* | |
2155 * free_tree () | |
2156 * recurse on left children, iterate on right children. | |
2157 */ | |
442 | 2158 static void |
428 | 2159 free_tree (np) |
2160 register node *np; | |
2161 { | |
2162 while (np) | |
2163 { | |
2164 register node *node_right = np->right; | |
2165 free_tree (np->left); | |
2166 if (np->name != NULL) | |
2167 free (np->name); | |
2225 | 2168 free (np->regex); |
428 | 2169 free (np); |
2170 np = node_right; | |
2171 } | |
2172 } | |
2173 | |
2174 /* | |
2225 | 2175 * free_fdesc () |
2176 * delete a file description | |
2177 */ | |
2178 static void | |
2179 free_fdesc (fdp) | |
2180 register fdesc *fdp; | |
2181 { | |
2182 if (fdp->infname != NULL) free (fdp->infname); | |
2183 if (fdp->infabsname != NULL) free (fdp->infabsname); | |
2184 if (fdp->infabsdir != NULL) free (fdp->infabsdir); | |
2185 if (fdp->taggedfname != NULL) free (fdp->taggedfname); | |
2186 if (fdp->prop != NULL) free (fdp->prop); | |
2187 free (fdp); | |
2188 } | |
2189 | |
2190 /* | |
428 | 2191 * add_node () |
2225 | 2192 * Adds a node to the tree of nodes. In etags mode, sort by file |
2193 * name. In ctags mode, sort by tag name. Make no attempt at | |
2194 * balancing. | |
428 | 2195 * |
2196 * add_node is the only function allowed to add nodes, so it can | |
2197 * maintain state. | |
2198 */ | |
442 | 2199 static void |
428 | 2200 add_node (np, cur_node_p) |
2201 node *np, **cur_node_p; | |
2202 { | |
2203 register int dif; | |
2204 register node *cur_node = *cur_node_p; | |
2205 | |
2206 if (cur_node == NULL) | |
2207 { | |
2208 *cur_node_p = np; | |
2209 last_node = np; | |
2210 return; | |
2211 } | |
2212 | |
2213 if (!CTAGS) | |
2225 | 2214 /* Etags Mode */ |
428 | 2215 { |
2225 | 2216 /* For each file name, tags are in a linked sublist on the right |
2217 pointer. The first tags of different files are a linked list | |
2218 on the left pointer. last_node points to the end of the last | |
2219 used sublist. */ | |
2220 if (last_node != NULL && last_node->fdp == np->fdp) | |
2221 { | |
2222 /* Let's use the same sublist as the last added node. */ | |
2223 assert (last_node->right == NULL); | |
2224 last_node->right = np; | |
2225 last_node = np; | |
2226 } | |
2227 else if (cur_node->fdp == np->fdp) | |
2228 { | |
2229 /* Scanning the list we found the head of a sublist which is | |
2230 good for us. Let's scan this sublist. */ | |
2231 add_node (np, &cur_node->right); | |
2232 } | |
2233 else | |
2234 /* The head of this sublist is not good for us. Let's try the | |
2235 next one. */ | |
2236 add_node (np, &cur_node->left); | |
2237 } /* if ETAGS mode */ | |
2238 | |
428 | 2239 else |
2240 { | |
2241 /* Ctags Mode */ | |
2242 dif = strcmp (np->name, cur_node->name); | |
2243 | |
2244 /* | |
2245 * If this tag name matches an existing one, then | |
2246 * do not add the node, but maybe print a warning. | |
2247 */ | |
3876 | 2248 if (no_duplicates && !dif) |
428 | 2249 { |
2225 | 2250 if (np->fdp == cur_node->fdp) |
428 | 2251 { |
2252 if (!no_warnings) | |
2253 { | |
2254 fprintf (stderr, "Duplicate entry in file %s, line %d: %s\n", | |
2225 | 2255 np->fdp->infname, lineno, np->name); |
428 | 2256 fprintf (stderr, "Second entry ignored\n"); |
2257 } | |
2258 } | |
2259 else if (!cur_node->been_warned && !no_warnings) | |
2260 { | |
2261 fprintf | |
2262 (stderr, | |
2263 "Duplicate entry in files %s and %s: %s (Warning only)\n", | |
2225 | 2264 np->fdp->infname, cur_node->fdp->infname, np->name); |
428 | 2265 cur_node->been_warned = TRUE; |
2266 } | |
2267 return; | |
2268 } | |
2269 | |
2270 /* Actually add the node */ | |
2271 add_node (np, dif < 0 ? &cur_node->left : &cur_node->right); | |
2225 | 2272 } /* if CTAGS mode */ |
2273 } | |
2274 | |
2275 /* | |
2276 * invalidate_nodes () | |
2277 * Scan the node tree and invalidate all nodes pointing to the | |
2278 * given file description (CTAGS case) or free them (ETAGS case). | |
2279 */ | |
2280 static void | |
2281 invalidate_nodes (badfdp, npp) | |
2282 fdesc *badfdp; | |
2283 node **npp; | |
2284 { | |
2285 node *np = *npp; | |
2286 | |
2287 if (np == NULL) | |
2288 return; | |
2289 | |
2290 if (CTAGS) | |
2291 { | |
2292 if (np->left != NULL) | |
2293 invalidate_nodes (badfdp, &np->left); | |
2294 if (np->fdp == badfdp) | |
2295 np->valid = FALSE; | |
2296 if (np->right != NULL) | |
2297 invalidate_nodes (badfdp, &np->right); | |
2298 } | |
2299 else | |
2300 { | |
2301 assert (np->fdp != NULL); | |
2302 if (np->fdp == badfdp) | |
2303 { | |
2304 *npp = np->left; /* detach the sublist from the list */ | |
2305 np->left = NULL; /* isolate it */ | |
2306 free_tree (np); /* free it */ | |
2307 invalidate_nodes (badfdp, npp); | |
2308 } | |
2309 else | |
2310 invalidate_nodes (badfdp, &np->left); | |
428 | 2311 } |
2312 } | |
458 | 2313 |
428 | 2314 |
2225 | 2315 static int total_size_of_entries __P((node *)); |
2316 static int number_len __P((long)); | |
2317 | |
2318 /* Length of a non-negative number's decimal representation. */ | |
428 | 2319 static int |
2320 number_len (num) | |
2321 long num; | |
2322 { | |
2323 int len = 1; | |
2324 while ((num /= 10) > 0) | |
2325 len += 1; | |
2326 return len; | |
2327 } | |
2328 | |
2329 /* | |
2330 * Return total number of characters that put_entries will output for | |
2225 | 2331 * the nodes in the linked list at the right of the specified node. |
2332 * This count is irrelevant with etags.el since emacs 19.34 at least, | |
2333 * but is still supplied for backward compatibility. | |
428 | 2334 */ |
442 | 2335 static int |
428 | 2336 total_size_of_entries (np) |
2337 register node *np; | |
2338 { | |
2225 | 2339 register int total = 0; |
2340 | |
2341 for (; np != NULL; np = np->right) | |
2342 if (np->valid) | |
2343 { | |
2344 total += strlen (np->regex) + 1; /* pat\177 */ | |
2345 if (np->name != NULL) | |
2346 total += strlen (np->name) + 1; /* name\001 */ | |
2347 total += number_len ((long) np->lno) + 1; /* lno, */ | |
2348 if (np->cno != invalidcharno) /* cno */ | |
2349 total += number_len (np->cno); | |
2350 total += 1; /* newline */ | |
2351 } | |
2352 | |
2353 return total; | |
2354 } | |
2355 | |
2356 static void | |
2357 put_entries (np) | |
2358 register node *np; | |
2359 { | |
2360 register char *sp; | |
2361 static fdesc *fdp = NULL; | |
428 | 2362 |
2363 if (np == NULL) | |
2225 | 2364 return; |
2365 | |
2366 /* Output subentries that precede this one */ | |
2367 if (CTAGS) | |
2368 put_entries (np->left); | |
2369 | |
2370 /* Output this entry */ | |
2371 if (np->valid) | |
428 | 2372 { |
2225 | 2373 if (!CTAGS) |
2374 { | |
2375 /* Etags mode */ | |
2376 if (fdp != np->fdp) | |
2377 { | |
2378 fdp = np->fdp; | |
2379 fprintf (tagf, "\f\n%s,%d\n", | |
2380 fdp->taggedfname, total_size_of_entries (np)); | |
2381 fdp->written = TRUE; | |
2382 } | |
2383 fputs (np->regex, tagf); | |
2384 fputc ('\177', tagf); | |
2385 if (np->name != NULL) | |
2386 { | |
2387 fputs (np->name, tagf); | |
2388 fputc ('\001', tagf); | |
2389 } | |
2390 fprintf (tagf, "%d,", np->lno); | |
2391 if (np->cno != invalidcharno) | |
2392 fprintf (tagf, "%ld", np->cno); | |
2393 fputs ("\n", tagf); | |
2394 } | |
2395 else | |
2396 { | |
2397 /* Ctags mode */ | |
2398 if (np->name == NULL) | |
2399 error ("internal error: NULL name in ctags mode.", (char *)NULL); | |
2400 | |
2401 if (cxref_style) | |
2402 { | |
2403 if (vgrind_style) | |
2404 fprintf (stdout, "%s %s %d\n", | |
2405 np->name, np->fdp->taggedfname, (np->lno + 63) / 64); | |
2406 else | |
2407 fprintf (stdout, "%-16s %3d %-16s %s\n", | |
2408 np->name, np->lno, np->fdp->taggedfname, np->regex); | |
2409 } | |
2410 else | |
2411 { | |
2412 fprintf (tagf, "%s\t%s\t", np->name, np->fdp->taggedfname); | |
2413 | |
2414 if (np->is_func) | |
2415 { /* function or #define macro with args */ | |
2416 putc (searchar, tagf); | |
2417 putc ('^', tagf); | |
2418 | |
2419 for (sp = np->regex; *sp; sp++) | |
2420 { | |
2421 if (*sp == '\\' || *sp == searchar) | |
2422 putc ('\\', tagf); | |
2423 putc (*sp, tagf); | |
2424 } | |
2425 putc (searchar, tagf); | |
2426 } | |
2427 else | |
2428 { /* anything else; text pattern inadequate */ | |
2429 fprintf (tagf, "%d", np->lno); | |
2430 } | |
2431 putc ('\n', tagf); | |
2432 } | |
2433 } | |
2434 } /* if this node contains a valid tag */ | |
2435 | |
2436 /* Output subentries that follow this one */ | |
2437 put_entries (np->right); | |
2438 if (!CTAGS) | |
2439 put_entries (np->left); | |
428 | 2440 } |
458 | 2441 |
428 | 2442 |
458 | 2443 /* C extensions. */ |
2444 #define C_EXT 0x00fff /* C extensions */ | |
2445 #define C_PLAIN 0x00000 /* C */ | |
2446 #define C_PLPL 0x00001 /* C++ */ | |
2447 #define C_STAR 0x00003 /* C* */ | |
2448 #define C_JAVA 0x00005 /* JAVA */ | |
2449 #define C_AUTO 0x01000 /* C, but switch to C++ if `class' is met */ | |
2450 #define YACC 0x10000 /* yacc file */ | |
2451 | |
428 | 2452 /* |
2453 * The C symbol tables. | |
2454 */ | |
2455 enum sym_type | |
2456 { | |
2457 st_none, | |
2458 st_C_objprot, st_C_objimpl, st_C_objend, | |
2459 st_C_gnumacro, | |
2325 | 2460 st_C_ignore, st_C_attribute, |
428 | 2461 st_C_javastruct, |
2462 st_C_operator, | |
531 | 2463 st_C_class, st_C_template, |
2325 | 2464 st_C_struct, st_C_extern, st_C_enum, st_C_define, st_C_typedef |
428 | 2465 }; |
2466 | |
709 | 2467 static unsigned int hash __P((const char *, unsigned int)); |
2468 static struct C_stab_entry * in_word_set __P((const char *, unsigned int)); | |
2469 static enum sym_type C_symtype __P((char *, int, int)); | |
442 | 2470 |
428 | 2471 /* Feed stuff between (but not including) %[ and %] lines to: |
2325 | 2472 gperf -m 5 |
428 | 2473 %[ |
2325 | 2474 %compare-strncmp |
2475 %enum | |
2476 %struct-type | |
428 | 2477 struct C_stab_entry { char *name; int c_ext; enum sym_type type; } |
2478 %% | |
2325 | 2479 if, 0, st_C_ignore |
2480 for, 0, st_C_ignore | |
2481 while, 0, st_C_ignore | |
2482 switch, 0, st_C_ignore | |
2483 return, 0, st_C_ignore | |
2484 __attribute__, 0, st_C_attribute | |
2485 @interface, 0, st_C_objprot | |
2486 @protocol, 0, st_C_objprot | |
2487 @implementation,0, st_C_objimpl | |
2488 @end, 0, st_C_objend | |
3876 | 2489 import, (C_JAVA & ~C_PLPL), st_C_ignore |
2490 package, (C_JAVA & ~C_PLPL), st_C_ignore | |
2325 | 2491 friend, C_PLPL, st_C_ignore |
3876 | 2492 extends, (C_JAVA & ~C_PLPL), st_C_javastruct |
2493 implements, (C_JAVA & ~C_PLPL), st_C_javastruct | |
2494 interface, (C_JAVA & ~C_PLPL), st_C_struct | |
2325 | 2495 class, 0, st_C_class |
2496 namespace, C_PLPL, st_C_struct | |
2497 domain, C_STAR, st_C_struct | |
2498 union, 0, st_C_struct | |
2499 struct, 0, st_C_struct | |
2500 extern, 0, st_C_extern | |
2501 enum, 0, st_C_enum | |
2502 typedef, 0, st_C_typedef | |
2503 define, 0, st_C_define | |
3517 | 2504 undef, 0, st_C_define |
2325 | 2505 operator, C_PLPL, st_C_operator |
2506 template, 0, st_C_template | |
428 | 2507 # DEFUN used in emacs, the next three used in glibc (SYSCALL only for mach). |
2325 | 2508 DEFUN, 0, st_C_gnumacro |
2509 SYSCALL, 0, st_C_gnumacro | |
2510 ENTRY, 0, st_C_gnumacro | |
2511 PSEUDO, 0, st_C_gnumacro | |
428 | 2512 # These are defined inside C functions, so currently they are not met. |
2513 # EXFUN used in glibc, DEFVAR_* in emacs. | |
2325 | 2514 #EXFUN, 0, st_C_gnumacro |
2515 #DEFVAR_, 0, st_C_gnumacro | |
428 | 2516 %] |
2325 | 2517 and replace lines between %< and %> with its output, then: |
2518 - remove the #if characterset check | |
2519 - make in_word_set static and not inline. */ | |
428 | 2520 /*%<*/ |
2325 | 2521 /* C code produced by gperf version 3.0.1 */ |
2522 /* Command-line: gperf -m 5 */ | |
3517 | 2523 /* Computed positions: -k'2-3' */ |
2325 | 2524 |
428 | 2525 struct C_stab_entry { char *name; int c_ext; enum sym_type type; }; |
3517 | 2526 /* maximum key range = 33, duplicates = 0 */ |
428 | 2527 |
2528 #ifdef __GNUC__ | |
2529 __inline | |
2325 | 2530 #else |
2531 #ifdef __cplusplus | |
2532 inline | |
2533 #endif | |
428 | 2534 #endif |
2535 static unsigned int | |
2536 hash (str, len) | |
2537 register const char *str; | |
2538 register unsigned int len; | |
2539 { | |
2540 static unsigned char asso_values[] = | |
2541 { | |
3517 | 2542 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, |
2543 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2544 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2545 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2546 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2547 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2548 35, 35, 35, 35, 35, 35, 35, 35, 35, 15, | |
2549 14, 35, 35, 35, 35, 35, 35, 35, 14, 35, | |
2550 35, 35, 35, 12, 13, 35, 35, 35, 35, 12, | |
2551 35, 35, 35, 35, 35, 1, 35, 16, 35, 6, | |
2552 23, 0, 0, 35, 22, 0, 35, 35, 5, 0, | |
2553 0, 15, 1, 35, 6, 35, 8, 19, 35, 16, | |
2554 4, 5, 35, 35, 35, 35, 35, 35, 35, 35, | |
2555 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2556 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2557 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2558 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2559 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2560 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2561 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2562 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2563 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2564 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2565 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2566 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | |
2567 35, 35, 35, 35, 35, 35 | |
428 | 2568 }; |
3517 | 2569 register int hval = len; |
2570 | |
2571 switch (hval) | |
2572 { | |
2573 default: | |
2574 hval += asso_values[(unsigned char)str[2]]; | |
2575 /*FALLTHROUGH*/ | |
2576 case 2: | |
2577 hval += asso_values[(unsigned char)str[1]]; | |
2578 break; | |
2579 } | |
2580 return hval; | |
428 | 2581 } |
2582 | |
2583 static struct C_stab_entry * | |
2584 in_word_set (str, len) | |
2585 register const char *str; | |
2586 register unsigned int len; | |
2587 { | |
2325 | 2588 enum |
2589 { | |
3517 | 2590 TOTAL_KEYWORDS = 32, |
2325 | 2591 MIN_WORD_LENGTH = 2, |
2592 MAX_WORD_LENGTH = 15, | |
3517 | 2593 MIN_HASH_VALUE = 2, |
2594 MAX_HASH_VALUE = 34 | |
2325 | 2595 }; |
2596 | |
428 | 2597 static struct C_stab_entry wordlist[] = |
2598 { | |
3517 | 2599 {""}, {""}, |
2325 | 2600 {"if", 0, st_C_ignore}, |
3517 | 2601 {""}, |
2325 | 2602 {"@end", 0, st_C_objend}, |
3517 | 2603 {"union", 0, st_C_struct}, |
2604 {"define", 0, st_C_define}, | |
3876 | 2605 {"import", (C_JAVA & ~C_PLPL), st_C_ignore}, |
3517 | 2606 {"template", 0, st_C_template}, |
2607 {"operator", C_PLPL, st_C_operator}, | |
2608 {"@interface", 0, st_C_objprot}, | |
3876 | 2609 {"implements", (C_JAVA & ~C_PLPL), st_C_javastruct}, |
3517 | 2610 {"friend", C_PLPL, st_C_ignore}, |
2611 {"typedef", 0, st_C_typedef}, | |
2612 {"return", 0, st_C_ignore}, | |
2613 {"@implementation",0, st_C_objimpl}, | |
2614 {"@protocol", 0, st_C_objprot}, | |
3876 | 2615 {"interface", (C_JAVA & ~C_PLPL), st_C_struct}, |
2325 | 2616 {"extern", 0, st_C_extern}, |
3876 | 2617 {"extends", (C_JAVA & ~C_PLPL), st_C_javastruct}, |
3517 | 2618 {"struct", 0, st_C_struct}, |
2619 {"domain", C_STAR, st_C_struct}, | |
2620 {"switch", 0, st_C_ignore}, | |
2621 {"enum", 0, st_C_enum}, | |
2325 | 2622 {"for", 0, st_C_ignore}, |
3517 | 2623 {"namespace", C_PLPL, st_C_struct}, |
2624 {"class", 0, st_C_class}, | |
2625 {"while", 0, st_C_ignore}, | |
2626 {"undef", 0, st_C_define}, | |
3876 | 2627 {"package", (C_JAVA & ~C_PLPL), st_C_ignore}, |
2325 | 2628 {"__attribute__", 0, st_C_attribute}, |
2629 {"SYSCALL", 0, st_C_gnumacro}, | |
3517 | 2630 {"ENTRY", 0, st_C_gnumacro}, |
2325 | 2631 {"PSEUDO", 0, st_C_gnumacro}, |
2632 {"DEFUN", 0, st_C_gnumacro} | |
428 | 2633 }; |
2634 | |
2635 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) | |
2636 { | |
2637 register int key = hash (str, len); | |
2638 | |
2639 if (key <= MAX_HASH_VALUE && key >= 0) | |
2640 { | |
2641 register const char *s = wordlist[key].name; | |
2642 | |
2325 | 2643 if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') |
428 | 2644 return &wordlist[key]; |
2645 } | |
2646 } | |
2647 return 0; | |
2648 } | |
2649 /*%>*/ | |
2650 | |
2651 static enum sym_type | |
2652 C_symtype (str, len, c_ext) | |
2653 char *str; | |
2654 int len; | |
2655 int c_ext; | |
2656 { | |
2657 register struct C_stab_entry *se = in_word_set (str, len); | |
2658 | |
2659 if (se == NULL || (se->c_ext && !(c_ext & se->c_ext))) | |
2660 return st_none; | |
2661 return se->type; | |
2662 } | |
458 | 2663 |
428 | 2664 |
458 | 2665 /* |
2325 | 2666 * Ignoring __attribute__ ((list)) |
2667 */ | |
2668 static bool inattribute; /* looking at an __attribute__ construct */ | |
2669 | |
2670 /* | |
458 | 2671 * C functions and variables are recognized using a simple |
2672 * finite automaton. fvdef is its state variable. | |
2673 */ | |
2225 | 2674 static enum |
428 | 2675 { |
2676 fvnone, /* nothing seen */ | |
458 | 2677 fdefunkey, /* Emacs DEFUN keyword seen */ |
2678 fdefunname, /* Emacs DEFUN name seen */ | |
428 | 2679 foperator, /* func: operator keyword seen (cplpl) */ |
2680 fvnameseen, /* function or variable name seen */ | |
2681 fstartlist, /* func: just after open parenthesis */ | |
2682 finlist, /* func: in parameter list */ | |
2683 flistseen, /* func: after parameter list */ | |
2684 fignore, /* func: before open brace */ | |
2685 vignore /* var-like: ignore until ';' */ | |
2686 } fvdef; | |
2687 | |
2225 | 2688 static bool fvextern; /* func or var: extern keyword seen; */ |
428 | 2689 |
458 | 2690 /* |
2691 * typedefs are recognized using a simple finite automaton. | |
2692 * typdef is its state variable. | |
2693 */ | |
2225 | 2694 static enum |
428 | 2695 { |
2696 tnone, /* nothing seen */ | |
2697 tkeyseen, /* typedef keyword seen */ | |
2698 ttypeseen, /* defined type seen */ | |
2699 tinbody, /* inside typedef body */ | |
2700 tend, /* just before typedef tag */ | |
2701 tignore /* junk after typedef tag */ | |
2702 } typdef; | |
2703 | |
458 | 2704 /* |
2705 * struct-like structures (enum, struct and union) are recognized | |
2706 * using another simple finite automaton. `structdef' is its state | |
2707 * variable. | |
2708 */ | |
2225 | 2709 static enum |
428 | 2710 { |
458 | 2711 snone, /* nothing seen yet, |
2325 | 2712 or in struct body if bracelev > 0 */ |
428 | 2713 skeyseen, /* struct-like keyword seen */ |
2714 stagseen, /* struct-like tag seen */ | |
458 | 2715 scolonseen /* colon seen after struct-like tag */ |
428 | 2716 } structdef; |
2717 | |
2718 /* | |
2719 * When objdef is different from onone, objtag is the name of the class. | |
2720 */ | |
2225 | 2721 static char *objtag = "<uninited>"; |
428 | 2722 |
2723 /* | |
2724 * Yet another little state machine to deal with preprocessor lines. | |
2725 */ | |
2225 | 2726 static enum |
428 | 2727 { |
2728 dnone, /* nothing seen */ | |
2729 dsharpseen, /* '#' seen as first char on line */ | |
2730 ddefineseen, /* '#' and 'define' seen */ | |
2731 dignorerest /* ignore rest of line */ | |
2732 } definedef; | |
2733 | |
2734 /* | |
2735 * State machine for Objective C protocols and implementations. | |
458 | 2736 * Idea by Tom R.Hageman <tom@basil.icce.rug.nl> (1995) |
428 | 2737 */ |
2225 | 2738 static enum |
428 | 2739 { |
2740 onone, /* nothing seen */ | |
2741 oprotocol, /* @interface or @protocol seen */ | |
2742 oimplementation, /* @implementations seen */ | |
2743 otagseen, /* class name seen */ | |
2744 oparenseen, /* parenthesis before category seen */ | |
2745 ocatseen, /* category name seen */ | |
2746 oinbody, /* in @implementation body */ | |
2747 omethodsign, /* in @implementation body, after +/- */ | |
2748 omethodtag, /* after method name */ | |
2749 omethodcolon, /* after method colon */ | |
2750 omethodparm, /* after method parameter */ | |
2751 oignore /* wait for @end */ | |
2752 } objdef; | |
2753 | |
2754 | |
2755 /* | |
2756 * Use this structure to keep info about the token read, and how it | |
2757 * should be tagged. Used by the make_C_tag function to build a tag. | |
2758 */ | |
2225 | 2759 static struct tok |
428 | 2760 { |
2225 | 2761 char *line; /* string containing the token */ |
2762 int offset; /* where the token starts in LINE */ | |
2763 int length; /* token length */ | |
2764 /* | |
2765 The previous members can be used to pass strings around for generic | |
2766 purposes. The following ones specifically refer to creating tags. In this | |
2767 case the token contained here is the pattern that will be used to create a | |
2768 tag. | |
2769 */ | |
2770 bool valid; /* do not create a tag; the token should be | |
2771 invalidated whenever a state machine is | |
2772 reset prematurely */ | |
2773 bool named; /* create a named tag */ | |
2774 int lineno; /* source line number of tag */ | |
2775 long linepos; /* source char number of tag */ | |
458 | 2776 } token; /* latest token read */ |
428 | 2777 |
2778 /* | |
458 | 2779 * Variables and functions for dealing with nested structures. |
2780 * Idea by Mykola Dzyuba <mdzyuba@yahoo.com> (2001) | |
428 | 2781 */ |
709 | 2782 static void pushclass_above __P((int, char *, int)); |
2783 static void popclass_above __P((int)); | |
2784 static void write_classname __P((linebuffer *, char *qualifier)); | |
458 | 2785 |
2225 | 2786 static struct { |
458 | 2787 char **cname; /* nested class names */ |
2325 | 2788 int *bracelev; /* nested class brace level */ |
458 | 2789 int nl; /* class nesting level (elements used) */ |
2790 int size; /* length of the array */ | |
2791 } cstack; /* stack for nested declaration tags */ | |
2792 /* Current struct nesting depth (namespace, class, struct, union, enum). */ | |
2793 #define nestlev (cstack.nl) | |
2225 | 2794 /* After struct keyword or in struct body, not inside a nested function. */ |
458 | 2795 #define instruct (structdef == snone && nestlev > 0 \ |
2325 | 2796 && bracelev == cstack.bracelev[nestlev-1] + 1) |
458 | 2797 |
2798 static void | |
2325 | 2799 pushclass_above (bracelev, str, len) |
2800 int bracelev; | |
458 | 2801 char *str; |
2802 int len; | |
2803 { | |
2804 int nl; | |
2805 | |
2325 | 2806 popclass_above (bracelev); |
458 | 2807 nl = cstack.nl; |
2808 if (nl >= cstack.size) | |
2809 { | |
2810 int size = cstack.size *= 2; | |
2811 xrnew (cstack.cname, size, char *); | |
2325 | 2812 xrnew (cstack.bracelev, size, int); |
458 | 2813 } |
2325 | 2814 assert (nl == 0 || cstack.bracelev[nl-1] < bracelev); |
458 | 2815 cstack.cname[nl] = (str == NULL) ? NULL : savenstr (str, len); |
2325 | 2816 cstack.bracelev[nl] = bracelev; |
458 | 2817 cstack.nl = nl + 1; |
2818 } | |
2819 | |
2820 static void | |
2325 | 2821 popclass_above (bracelev) |
2822 int bracelev; | |
458 | 2823 { |
2824 int nl; | |
2825 | |
2826 for (nl = cstack.nl - 1; | |
2325 | 2827 nl >= 0 && cstack.bracelev[nl] >= bracelev; |
458 | 2828 nl--) |
2829 { | |
2830 if (cstack.cname[nl] != NULL) | |
2831 free (cstack.cname[nl]); | |
2832 cstack.nl = nl; | |
2833 } | |
2834 } | |
2835 | |
2836 static void | |
2837 write_classname (cn, qualifier) | |
2838 linebuffer *cn; | |
2839 char *qualifier; | |
2840 { | |
2841 int i, len; | |
2842 int qlen = strlen (qualifier); | |
2843 | |
2844 if (cstack.nl == 0 || cstack.cname[0] == NULL) | |
2845 { | |
2846 len = 0; | |
2847 cn->len = 0; | |
2848 cn->buffer[0] = '\0'; | |
2849 } | |
2850 else | |
2851 { | |
2852 len = strlen (cstack.cname[0]); | |
2853 linebuffer_setlen (cn, len); | |
2854 strcpy (cn->buffer, cstack.cname[0]); | |
2855 } | |
2856 for (i = 1; i < cstack.nl; i++) | |
2857 { | |
2858 char *s; | |
2859 int slen; | |
2860 | |
2861 s = cstack.cname[i]; | |
2862 if (s == NULL) | |
2863 continue; | |
2864 slen = strlen (s); | |
2865 len += slen + qlen; | |
2866 linebuffer_setlen (cn, len); | |
2867 strncat (cn->buffer, qualifier, qlen); | |
2868 strncat (cn->buffer, s, slen); | |
2869 } | |
2870 } | |
2871 | |
2872 | |
709 | 2873 static bool consider_token __P((char *, int, int, int *, int, int, bool *)); |
2874 static void make_C_tag __P((bool)); | |
442 | 2875 |
428 | 2876 /* |
2877 * consider_token () | |
2878 * checks to see if the current token is at the start of a | |
2879 * function or variable, or corresponds to a typedef, or | |
2880 * is a struct/union/enum tag, or #define, or an enum constant. | |
2881 * | |
2882 * *IS_FUNC gets TRUE iff the token is a function or #define macro | |
458 | 2883 * with args. C_EXTP points to which language we are looking at. |
428 | 2884 * |
2885 * Globals | |
2886 * fvdef IN OUT | |
2887 * structdef IN OUT | |
2888 * definedef IN OUT | |
2889 * typdef IN OUT | |
2890 * objdef IN OUT | |
2891 */ | |
2892 | |
2893 static bool | |
2325 | 2894 consider_token (str, len, c, c_extp, bracelev, parlev, is_func_or_var) |
428 | 2895 register char *str; /* IN: token pointer */ |
2896 register int len; /* IN: token length */ | |
442 | 2897 register int c; /* IN: first char after the token */ |
458 | 2898 int *c_extp; /* IN, OUT: C extensions mask */ |
2325 | 2899 int bracelev; /* IN: brace level */ |
428 | 2900 int parlev; /* IN: parenthesis level */ |
2901 bool *is_func_or_var; /* OUT: function or variable found */ | |
2902 { | |
2325 | 2903 /* When structdef is stagseen, scolonseen, or snone with bracelev > 0, |
458 | 2904 structtype is the type of the preceding struct-like keyword, and |
2325 | 2905 structbracelev is the brace level where it has been seen. */ |
458 | 2906 static enum sym_type structtype; |
2325 | 2907 static int structbracelev; |
458 | 2908 static enum sym_type toktype; |
2909 | |
2910 | |
2911 toktype = C_symtype (str, len, *c_extp); | |
428 | 2912 |
2913 /* | |
2325 | 2914 * Skip __attribute__ |
428 | 2915 */ |
2325 | 2916 if (toktype == st_C_attribute) |
428 | 2917 { |
2325 | 2918 inattribute = TRUE; |
428 | 2919 return FALSE; |
2325 | 2920 } |
2921 | |
2922 /* | |
2923 * Advance the definedef state machine. | |
2924 */ | |
2925 switch (definedef) | |
2926 { | |
2927 case dnone: | |
2928 /* We're not on a preprocessor line. */ | |
2929 if (toktype == st_C_gnumacro) | |
2930 { | |
2931 fvdef = fdefunkey; | |
2932 return FALSE; | |
2933 } | |
2934 break; | |
2935 case dsharpseen: | |
2936 if (toktype == st_C_define) | |
2937 { | |
2938 definedef = ddefineseen; | |
2939 } | |
2940 else | |
2941 { | |
2942 definedef = dignorerest; | |
2943 } | |
2944 return FALSE; | |
2945 case ddefineseen: | |
2946 /* | |
2947 * Make a tag for any macro, unless it is a constant | |
2948 * and constantypedefs is FALSE. | |
2949 */ | |
2950 definedef = dignorerest; | |
2951 *is_func_or_var = (c == '('); | |
2952 if (!*is_func_or_var && !constantypedefs) | |
2953 return FALSE; | |
2954 else | |
2955 return TRUE; | |
2956 case dignorerest: | |
2957 return FALSE; | |
2958 default: | |
2959 error ("internal error: definedef value.", (char *)NULL); | |
2960 } | |
2961 | |
2962 /* | |
2963 * Now typedefs | |
2964 */ | |
2965 switch (typdef) | |
2966 { | |
2967 case tnone: | |
2968 if (toktype == st_C_typedef) | |
2969 { | |
2970 if (typedefs) | |
2971 typdef = tkeyseen; | |
2972 fvextern = FALSE; | |
2973 fvdef = fvnone; | |
2974 return FALSE; | |
2975 } | |
2976 break; | |
2977 case tkeyseen: | |
2978 switch (toktype) | |
2979 { | |
2980 case st_none: | |
2981 case st_C_class: | |
2982 case st_C_struct: | |
2983 case st_C_enum: | |
2984 typdef = ttypeseen; | |
2985 } | |
2986 break; | |
2987 case ttypeseen: | |
2988 if (structdef == snone && fvdef == fvnone) | |
2989 { | |
2990 fvdef = fvnameseen; | |
2991 return TRUE; | |
2992 } | |
2993 break; | |
2994 case tend: | |
2995 switch (toktype) | |
2996 { | |
2997 case st_C_class: | |
2998 case st_C_struct: | |
2999 case st_C_enum: | |
3000 return FALSE; | |
3001 } | |
3002 return TRUE; | |
3003 } | |
3004 | |
3005 /* | |
3006 * This structdef business is NOT invoked when we are ctags and the | |
3007 * file is plain C. This is because a struct tag may have the same | |
3008 * name as another tag, and this loses with ctags. | |
3009 */ | |
3010 switch (toktype) | |
3011 { | |
3012 case st_C_javastruct: | |
3013 if (structdef == stagseen) | |
3014 structdef = scolonseen; | |
3015 return FALSE; | |
3016 case st_C_template: | |
3017 case st_C_class: | |
3018 if ((*c_extp & C_AUTO) /* automatic detection of C++ language */ | |
3019 && bracelev == 0 | |
3020 && definedef == dnone && structdef == snone | |
3021 && typdef == tnone && fvdef == fvnone) | |
3022 *c_extp = (*c_extp | C_PLPL) & ~C_AUTO; | |
3023 if (toktype == st_C_template) | |
3024 break; | |
3025 /* FALLTHRU */ | |
3026 case st_C_struct: | |
3027 case st_C_enum: | |
3028 if (parlev == 0 | |
3029 && fvdef != vignore | |
3030 && (typdef == tkeyseen | |
3031 || (typedefs_or_cplusplus && structdef == snone))) | |
3032 { | |
3033 structdef = skeyseen; | |
3034 structtype = toktype; | |
3035 structbracelev = bracelev; | |
3036 if (fvdef == fvnameseen) | |
3037 fvdef = fvnone; | |
3038 } | |
3039 return FALSE; | |
3040 } | |
3041 | |
3042 if (structdef == skeyseen) | |
3043 { | |
3044 structdef = stagseen; | |
3045 return TRUE; | |
3046 } | |
3047 | |
3048 if (typdef != tnone) | |
3049 definedef = dnone; | |
3050 | |
3051 /* Detect Objective C constructs. */ | |
3052 switch (objdef) | |
3053 { | |
3054 case onone: | |
3055 switch (toktype) | |
3056 { | |
3057 case st_C_objprot: | |
3058 objdef = oprotocol; | |
3059 return FALSE; | |
3060 case st_C_objimpl: | |
3061 objdef = oimplementation; | |
3062 return FALSE; | |
3063 } | |
3064 break; | |
3065 case oimplementation: | |
3066 /* Save the class tag for functions or variables defined inside. */ | |
3067 objtag = savenstr (str, len); | |
3068 objdef = oinbody; | |
3069 return FALSE; | |
3070 case oprotocol: | |
3071 /* Save the class tag for categories. */ | |
3072 objtag = savenstr (str, len); | |
3073 objdef = otagseen; | |
3074 *is_func_or_var = TRUE; | |
3075 return TRUE; | |
3076 case oparenseen: | |
3077 objdef = ocatseen; | |
3078 *is_func_or_var = TRUE; | |
3079 return TRUE; | |
3080 case oinbody: | |
3081 break; | |
3082 case omethodsign: | |
3083 if (parlev == 0) | |
3084 { | |
3085 fvdef = fvnone; | |
3086 objdef = omethodtag; | |
3087 linebuffer_setlen (&token_name, len); | |
3088 strncpy (token_name.buffer, str, len); | |
3089 token_name.buffer[len] = '\0'; | |
3090 return TRUE; | |
3091 } | |
3092 return FALSE; | |
3093 case omethodcolon: | |
3094 if (parlev == 0) | |
3095 objdef = omethodparm; | |
3096 return FALSE; | |
3097 case omethodparm: | |
3098 if (parlev == 0) | |
3099 { | |
3100 fvdef = fvnone; | |
3101 objdef = omethodtag; | |
3102 linebuffer_setlen (&token_name, token_name.len + len); | |
3103 strncat (token_name.buffer, str, len); | |
3104 return TRUE; | |
3105 } | |
3106 return FALSE; | |
3107 case oignore: | |
3108 if (toktype == st_C_objend) | |
3109 { | |
3110 /* Memory leakage here: the string pointed by objtag is | |
3111 never released, because many tests would be needed to | |
3112 avoid breaking on incorrect input code. The amount of | |
3113 memory leaked here is the sum of the lengths of the | |
3114 class tags. | |
3115 free (objtag); */ | |
3116 objdef = onone; | |
3117 } | |
3118 return FALSE; | |
3119 } | |
3120 | |
3121 /* A function, variable or enum constant? */ | |
3122 switch (toktype) | |
3123 { | |
3124 case st_C_extern: | |
3125 fvextern = TRUE; | |
3126 switch (fvdef) | |
3127 { | |
3128 case finlist: | |
3129 case flistseen: | |
3130 case fignore: | |
3131 case vignore: | |
3132 break; | |
3133 default: | |
3134 fvdef = fvnone; | |
3135 } | |
3136 return FALSE; | |
3137 case st_C_ignore: | |
3138 fvextern = FALSE; | |
3139 fvdef = vignore; | |
3140 return FALSE; | |
3141 case st_C_operator: | |
3142 fvdef = foperator; | |
3143 *is_func_or_var = TRUE; | |
3144 return TRUE; | |
3145 case st_none: | |
3146 if (constantypedefs | |
3147 && structdef == snone | |
3148 && structtype == st_C_enum && bracelev > structbracelev) | |
3149 return TRUE; /* enum constant */ | |
3150 switch (fvdef) | |
3151 { | |
3152 case fdefunkey: | |
3153 if (bracelev > 0) | |
3154 break; | |
3155 fvdef = fdefunname; /* GNU macro */ | |
3156 *is_func_or_var = TRUE; | |
3157 return TRUE; | |
3158 case fvnone: | |
3159 switch (typdef) | |
3160 { | |
3161 case ttypeseen: | |
3162 return FALSE; | |
3163 case tnone: | |
3164 if ((strneq (str, "asm", 3) && endtoken (str[3])) | |
3165 || (strneq (str, "__asm__", 7) && endtoken (str[7]))) | |
3166 { | |
3167 fvdef = vignore; | |
3168 return FALSE; | |
3169 } | |
3170 break; | |
3171 } | |
3172 /* FALLTHRU */ | |
3173 case fvnameseen: | |
2225 | 3174 if (len >= 10 && strneq (str+len-10, "::operator", 10)) |
458 | 3175 { |
2225 | 3176 if (*c_extp & C_AUTO) /* automatic detection of C++ */ |
3177 *c_extp = (*c_extp | C_PLPL) & ~C_AUTO; | |
458 | 3178 fvdef = foperator; |
3179 *is_func_or_var = TRUE; | |
3180 return TRUE; | |
3181 } | |
2325 | 3182 if (bracelev > 0 && !instruct) |
458 | 3183 break; |
428 | 3184 fvdef = fvnameseen; /* function or variable */ |
3185 *is_func_or_var = TRUE; | |
3186 return TRUE; | |
3187 } | |
3188 break; | |
3189 } | |
3190 | |
3191 return FALSE; | |
3192 } | |
3193 | |
458 | 3194 |
428 | 3195 /* |
458 | 3196 * C_entries often keeps pointers to tokens or lines which are older than |
3197 * the line currently read. By keeping two line buffers, and switching | |
3198 * them at end of line, it is possible to use those pointers. | |
428 | 3199 */ |
2225 | 3200 static struct |
458 | 3201 { |
3202 long linepos; | |
3203 linebuffer lb; | |
3204 } lbs[2]; | |
3205 | |
428 | 3206 #define current_lb_is_new (newndx == curndx) |
3207 #define switch_line_buffers() (curndx = 1 - curndx) | |
3208 | |
3209 #define curlb (lbs[curndx].lb) | |
3210 #define newlb (lbs[newndx].lb) | |
3211 #define curlinepos (lbs[curndx].linepos) | |
3212 #define newlinepos (lbs[newndx].linepos) | |
3213 | |
2225 | 3214 #define plainc ((c_ext & C_EXT) == C_PLAIN) |
3215 #define cplpl (c_ext & C_PLPL) | |
3216 #define cjava ((c_ext & C_JAVA) == C_JAVA) | |
3217 | |
428 | 3218 #define CNL_SAVE_DEFINEDEF() \ |
3219 do { \ | |
3220 curlinepos = charno; \ | |
2225 | 3221 readline (&curlb, inf); \ |
428 | 3222 lp = curlb.buffer; \ |
3223 quotednl = FALSE; \ | |
3224 newndx = curndx; \ | |
3225 } while (0) | |
3226 | |
3227 #define CNL() \ | |
3228 do { \ | |
3229 CNL_SAVE_DEFINEDEF(); \ | |
458 | 3230 if (savetoken.valid) \ |
428 | 3231 { \ |
458 | 3232 token = savetoken; \ |
3233 savetoken.valid = FALSE; \ | |
428 | 3234 } \ |
3235 definedef = dnone; \ | |
3236 } while (0) | |
3237 | |
3238 | |
3239 static void | |
3240 make_C_tag (isfun) | |
3241 bool isfun; | |
3242 { | |
3876 | 3243 /* This function is never called when token.valid is FALSE, but |
428 | 3244 we must protect against invalid input or internal errors. */ |
2225 | 3245 if (!DEBUG && !token.valid) |
3246 return; | |
3247 | |
3248 if (token.valid) | |
3249 make_tag (token_name.buffer, token_name.len, isfun, token.line, | |
3250 token.offset+token.length+1, token.lineno, token.linepos); | |
3251 else /* this case is optimised away if !DEBUG */ | |
3252 make_tag (concat ("INVALID TOKEN:-->", token_name.buffer, ""), | |
3253 token_name.len + 17, isfun, token.line, | |
3254 token.offset+token.length+1, token.lineno, token.linepos); | |
3255 | |
3256 token.valid = FALSE; | |
428 | 3257 } |
3258 | |
3259 | |
458 | 3260 /* |
3261 * C_entries () | |
3262 * This routine finds functions, variables, typedefs, | |
3263 * #define's, enum constants and struct/union/enum definitions in | |
3264 * C syntax and adds them to the list. | |
3265 */ | |
442 | 3266 static void |
428 | 3267 C_entries (c_ext, inf) |
3268 int c_ext; /* extension of C */ | |
3269 FILE *inf; /* input file */ | |
3270 { | |
3271 register char c; /* latest char read; '\0' for end of line */ | |
3272 register char *lp; /* pointer one beyond the character `c' */ | |
3273 int curndx, newndx; /* indices for current and new lb */ | |
3274 register int tokoff; /* offset in line of start of current token */ | |
3275 register int toklen; /* length of current token */ | |
3276 char *qualifier; /* string used to qualify names */ | |
3277 int qlen; /* length of qualifier */ | |
2325 | 3278 int bracelev; /* current brace level */ |
3279 int bracketlev; /* current bracket level */ | |
428 | 3280 int parlev; /* current parenthesis level */ |
2325 | 3281 int attrparlev; /* __attribute__ parenthesis level */ |
3282 int templatelev; /* current template level */ | |
3283 int typdefbracelev; /* bracelev where a typedef struct body begun */ | |
428 | 3284 bool incomm, inquote, inchar, quotednl, midtoken; |
458 | 3285 bool yacc_rules; /* in the rules part of a yacc file */ |
3876 | 3286 struct tok savetoken = {0}; /* token saved during preprocessor handling */ |
458 | 3287 |
3288 | |
2225 | 3289 linebuffer_init (&lbs[0].lb); |
3290 linebuffer_init (&lbs[1].lb); | |
458 | 3291 if (cstack.size == 0) |
3292 { | |
3293 cstack.size = (DEBUG) ? 1 : 4; | |
3294 cstack.nl = 0; | |
3295 cstack.cname = xnew (cstack.size, char *); | |
2325 | 3296 cstack.bracelev = xnew (cstack.size, int); |
458 | 3297 } |
428 | 3298 |
2325 | 3299 tokoff = toklen = typdefbracelev = 0; /* keep compiler quiet */ |
428 | 3300 curndx = newndx = 0; |
3301 lp = curlb.buffer; | |
3302 *lp = 0; | |
3303 | |
3304 fvdef = fvnone; fvextern = FALSE; typdef = tnone; | |
3305 structdef = snone; definedef = dnone; objdef = onone; | |
458 | 3306 yacc_rules = FALSE; |
428 | 3307 midtoken = inquote = inchar = incomm = quotednl = FALSE; |
458 | 3308 token.valid = savetoken.valid = FALSE; |
2325 | 3309 bracelev = bracketlev = parlev = attrparlev = templatelev = 0; |
428 | 3310 if (cjava) |
3311 { qualifier = "."; qlen = 1; } | |
3312 else | |
3313 { qualifier = "::"; qlen = 2; } | |
3314 | |
458 | 3315 |
428 | 3316 while (!feof (inf)) |
3317 { | |
3318 c = *lp++; | |
3319 if (c == '\\') | |
3320 { | |
2325 | 3321 /* If we are at the end of the line, the next character is a |
3322 '\0'; do not skip it, because it is what tells us | |
428 | 3323 to read the next line. */ |
3324 if (*lp == '\0') | |
3325 { | |
3326 quotednl = TRUE; | |
3327 continue; | |
3328 } | |
3329 lp++; | |
3330 c = ' '; | |
3331 } | |
3332 else if (incomm) | |
3333 { | |
3334 switch (c) | |
3335 { | |
3336 case '*': | |
3337 if (*lp == '/') | |
3338 { | |
3339 c = *lp++; | |
3340 incomm = FALSE; | |
3341 } | |
3342 break; | |
3343 case '\0': | |
3344 /* Newlines inside comments do not end macro definitions in | |
3345 traditional cpp. */ | |
3346 CNL_SAVE_DEFINEDEF (); | |
3347 break; | |
3348 } | |
3349 continue; | |
3350 } | |
3351 else if (inquote) | |
3352 { | |
3353 switch (c) | |
3354 { | |
3355 case '"': | |
3356 inquote = FALSE; | |
3357 break; | |
3358 case '\0': | |
3359 /* Newlines inside strings do not end macro definitions | |
3360 in traditional cpp, even though compilers don't | |
3361 usually accept them. */ | |
3362 CNL_SAVE_DEFINEDEF (); | |
3363 break; | |
3364 } | |
3365 continue; | |
3366 } | |
3367 else if (inchar) | |
3368 { | |
3369 switch (c) | |
3370 { | |
3371 case '\0': | |
3372 /* Hmmm, something went wrong. */ | |
3373 CNL (); | |
3374 /* FALLTHRU */ | |
3375 case '\'': | |
3376 inchar = FALSE; | |
3377 break; | |
3378 } | |
3379 continue; | |
3380 } | |
2325 | 3381 else if (bracketlev > 0) |
3382 { | |
3383 switch (c) | |
3384 { | |
3385 case ']': | |
3386 if (--bracketlev > 0) | |
428 | 3387 continue; |
2325 | 3388 break; |
3389 case '\0': | |
3390 CNL_SAVE_DEFINEDEF (); | |
3391 break; | |
3392 } | |
3393 continue; | |
3394 } | |
3395 else switch (c) | |
3396 { | |
3397 case '"': | |
3398 inquote = TRUE; | |
3399 if (inattribute) | |
3400 break; | |
3401 switch (fvdef) | |
3402 { | |
3403 case fdefunkey: | |
3404 case fstartlist: | |
3405 case finlist: | |
3406 case fignore: | |
3407 case vignore: | |
428 | 3408 break; |
2325 | 3409 default: |
3410 fvextern = FALSE; | |
3411 fvdef = fvnone; | |
3412 } | |
3413 continue; | |
3414 case '\'': | |
3415 inchar = TRUE; | |
3416 if (inattribute) | |
3417 break; | |
3418 if (fvdef != finlist && fvdef != fignore && fvdef !=vignore) | |
3419 { | |
3420 fvextern = FALSE; | |
3421 fvdef = fvnone; | |
3422 } | |
3423 continue; | |
3424 case '/': | |
3425 if (*lp == '*') | |
3426 { | |
3972 | 3427 incomm = TRUE; |
2325 | 3428 lp++; |
3972 | 3429 c = ' '; |
2325 | 3430 } |
3431 else if (/* cplpl && */ *lp == '/') | |
3432 { | |
3433 c = '\0'; | |
3434 } | |
3972 | 3435 break; |
2325 | 3436 case '%': |
3437 if ((c_ext & YACC) && *lp == '%') | |
3438 { | |
3439 /* Entering or exiting rules section in yacc file. */ | |
3440 lp++; | |
3441 definedef = dnone; fvdef = fvnone; fvextern = FALSE; | |
3442 typdef = tnone; structdef = snone; | |
3443 midtoken = inquote = inchar = incomm = quotednl = FALSE; | |
3444 bracelev = 0; | |
3445 yacc_rules = !yacc_rules; | |
3446 continue; | |
3447 } | |
3448 else | |
3449 break; | |
3450 case '#': | |
3451 if (definedef == dnone) | |
3452 { | |
3453 char *cp; | |
3454 bool cpptoken = TRUE; | |
3455 | |
3456 /* Look back on this line. If all blanks, or nonblanks | |
3457 followed by an end of comment, this is a preprocessor | |
3458 token. */ | |
3459 for (cp = newlb.buffer; cp < lp-1; cp++) | |
3460 if (!iswhite (*cp)) | |
3461 { | |
3462 if (*cp == '*' && *(cp+1) == '/') | |
3463 { | |
3464 cp++; | |
3465 cpptoken = TRUE; | |
3466 } | |
3467 else | |
3468 cpptoken = FALSE; | |
3469 } | |
3470 if (cpptoken) | |
3471 definedef = dsharpseen; | |
3472 } /* if (definedef == dnone) */ | |
3473 continue; | |
3474 case '[': | |
3475 bracketlev++; | |
428 | 3476 continue; |
2325 | 3477 } /* switch (c) */ |
428 | 3478 |
3479 | |
458 | 3480 /* Consider token only if some involved conditions are satisfied. */ |
3481 if (typdef != tignore | |
428 | 3482 && definedef != dignorerest |
458 | 3483 && fvdef != finlist |
2325 | 3484 && templatelev == 0 |
458 | 3485 && (definedef != dnone |
2325 | 3486 || structdef != scolonseen) |
3487 && !inattribute) | |
428 | 3488 { |
3489 if (midtoken) | |
3490 { | |
3491 if (endtoken (c)) | |
3492 { | |
2225 | 3493 if (c == ':' && *lp == ':' && begtoken (lp[1])) |
3494 /* This handles :: in the middle, | |
3495 but not at the beginning of an identifier. | |
3496 Also, space-separated :: is not recognised. */ | |
428 | 3497 { |
2225 | 3498 if (c_ext & C_AUTO) /* automatic detection of C++ */ |
3499 c_ext = (c_ext | C_PLPL) & ~C_AUTO; | |
428 | 3500 lp += 2; |
3501 toklen += 2; | |
3502 c = lp[-1]; | |
458 | 3503 goto still_in_token; |
428 | 3504 } |
3505 else | |
3506 { | |
458 | 3507 bool funorvar = FALSE; |
3508 | |
428 | 3509 if (yacc_rules |
3510 || consider_token (newlb.buffer + tokoff, toklen, c, | |
2325 | 3511 &c_ext, bracelev, parlev, |
3512 &funorvar)) | |
428 | 3513 { |
3514 if (fvdef == foperator) | |
3515 { | |
3516 char *oldlp = lp; | |
3517 lp = skip_spaces (lp-1); | |
3518 if (*lp != '\0') | |
3519 lp += 1; | |
3520 while (*lp != '\0' | |
442 | 3521 && !iswhite (*lp) && *lp != '(') |
428 | 3522 lp += 1; |
3523 c = *lp++; | |
3524 toklen += lp - oldlp; | |
3525 } | |
458 | 3526 token.named = FALSE; |
2225 | 3527 if (!plainc |
458 | 3528 && nestlev > 0 && definedef == dnone) |
3529 /* in struct body */ | |
428 | 3530 { |
458 | 3531 write_classname (&token_name, qualifier); |
3532 linebuffer_setlen (&token_name, | |
3533 token_name.len+qlen+toklen); | |
428 | 3534 strcat (token_name.buffer, qualifier); |
3535 strncat (token_name.buffer, | |
3536 newlb.buffer + tokoff, toklen); | |
458 | 3537 token.named = TRUE; |
428 | 3538 } |
3539 else if (objdef == ocatseen) | |
3540 /* Objective C category */ | |
3541 { | |
3542 int len = strlen (objtag) + 2 + toklen; | |
458 | 3543 linebuffer_setlen (&token_name, len); |
428 | 3544 strcpy (token_name.buffer, objtag); |
3545 strcat (token_name.buffer, "("); | |
3546 strncat (token_name.buffer, | |
3547 newlb.buffer + tokoff, toklen); | |
3548 strcat (token_name.buffer, ")"); | |
458 | 3549 token.named = TRUE; |
428 | 3550 } |
3551 else if (objdef == omethodtag | |
3552 || objdef == omethodparm) | |
3553 /* Objective C method */ | |
3554 { | |
458 | 3555 token.named = TRUE; |
3556 } | |
3557 else if (fvdef == fdefunname) | |
3558 /* GNU DEFUN and similar macros */ | |
3559 { | |
3560 bool defun = (newlb.buffer[tokoff] == 'F'); | |
3561 int off = tokoff; | |
3562 int len = toklen; | |
3563 | |
3564 /* Rewrite the tag so that emacs lisp DEFUNs | |
3565 can be found by their elisp name */ | |
3566 if (defun) | |
3567 { | |
3568 off += 1; | |
3569 len -= 1; | |
3570 } | |
3571 linebuffer_setlen (&token_name, len); | |
3572 strncpy (token_name.buffer, | |
3573 newlb.buffer + off, len); | |
3574 token_name.buffer[len] = '\0'; | |
3575 if (defun) | |
3576 while (--len >= 0) | |
3577 if (token_name.buffer[len] == '_') | |
3578 token_name.buffer[len] = '-'; | |
3579 token.named = defun; | |
428 | 3580 } |
3581 else | |
3582 { | |
458 | 3583 linebuffer_setlen (&token_name, toklen); |
428 | 3584 strncpy (token_name.buffer, |
3585 newlb.buffer + tokoff, toklen); | |
3586 token_name.buffer[toklen] = '\0'; | |
3587 /* Name macros and members. */ | |
458 | 3588 token.named = (structdef == stagseen |
3589 || typdef == ttypeseen | |
3590 || typdef == tend | |
3591 || (funorvar | |
3592 && definedef == dignorerest) | |
3593 || (funorvar | |
3594 && definedef == dnone | |
3595 && structdef == snone | |
2325 | 3596 && bracelev > 0)); |
428 | 3597 } |
458 | 3598 token.lineno = lineno; |
3599 token.offset = tokoff; | |
3600 token.length = toklen; | |
3601 token.line = newlb.buffer; | |
3602 token.linepos = newlinepos; | |
3603 token.valid = TRUE; | |
428 | 3604 |
3605 if (definedef == dnone | |
3606 && (fvdef == fvnameseen | |
3607 || fvdef == foperator | |
3608 || structdef == stagseen | |
3609 || typdef == tend | |
458 | 3610 || typdef == ttypeseen |
428 | 3611 || objdef != onone)) |
3612 { | |
3613 if (current_lb_is_new) | |
3614 switch_line_buffers (); | |
3615 } | |
458 | 3616 else if (definedef != dnone |
3617 || fvdef == fdefunname | |
3618 || instruct) | |
428 | 3619 make_C_tag (funorvar); |
3620 } | |
2325 | 3621 else /* not yacc and consider_token failed */ |
3622 { | |
3623 if (inattribute && fvdef == fignore) | |
3624 { | |
3625 /* We have just met __attribute__ after a | |
3626 function parameter list: do not tag the | |
3627 function again. */ | |
3628 fvdef = fvnone; | |
3629 } | |
3630 } | |
428 | 3631 midtoken = FALSE; |
3632 } | |
3633 } /* if (endtoken (c)) */ | |
3634 else if (intoken (c)) | |
458 | 3635 still_in_token: |
428 | 3636 { |
3637 toklen++; | |
3638 continue; | |
3639 } | |
3640 } /* if (midtoken) */ | |
3641 else if (begtoken (c)) | |
3642 { | |
3643 switch (definedef) | |
3644 { | |
3645 case dnone: | |
3646 switch (fvdef) | |
3647 { | |
3648 case fstartlist: | |
2325 | 3649 /* This prevents tagging fb in |
3650 void (__attribute__((noreturn)) *fb) (void); | |
3651 Fixing this is not easy and not very important. */ | |
428 | 3652 fvdef = finlist; |
3653 continue; | |
3654 case flistseen: | |
2225 | 3655 if (plainc || declarations) |
3656 { | |
3657 make_C_tag (TRUE); /* a function */ | |
3658 fvdef = fignore; | |
3659 } | |
428 | 3660 break; |
3661 } | |
3662 if (structdef == stagseen && !cjava) | |
458 | 3663 { |
2325 | 3664 popclass_above (bracelev); |
458 | 3665 structdef = snone; |
3666 } | |
428 | 3667 break; |
3668 case dsharpseen: | |
458 | 3669 savetoken = token; |
2225 | 3670 break; |
428 | 3671 } |
3672 if (!yacc_rules || lp == newlb.buffer + 1) | |
3673 { | |
3674 tokoff = lp - 1 - newlb.buffer; | |
3675 toklen = 1; | |
3676 midtoken = TRUE; | |
3677 } | |
3678 continue; | |
3679 } /* if (begtoken) */ | |
3680 } /* if must look at token */ | |
3681 | |
3682 | |
3683 /* Detect end of line, colon, comma, semicolon and various braces | |
3684 after having handled a token.*/ | |
3685 switch (c) | |
3686 { | |
3687 case ':': | |
2325 | 3688 if (inattribute) |
3689 break; | |
458 | 3690 if (yacc_rules && token.offset == 0 && token.valid) |
3691 { | |
3692 make_C_tag (FALSE); /* a yacc function */ | |
3693 break; | |
3694 } | |
428 | 3695 if (definedef != dnone) |
3696 break; | |
3697 switch (objdef) | |
3698 { | |
3699 case otagseen: | |
3700 objdef = oignore; | |
3701 make_C_tag (TRUE); /* an Objective C class */ | |
3702 break; | |
3703 case omethodtag: | |
3704 case omethodparm: | |
3705 objdef = omethodcolon; | |
458 | 3706 linebuffer_setlen (&token_name, token_name.len + 1); |
428 | 3707 strcat (token_name.buffer, ":"); |
3708 break; | |
3709 } | |
3710 if (structdef == stagseen) | |
2225 | 3711 { |
3712 structdef = scolonseen; | |
3713 break; | |
3714 } | |
3715 /* Should be useless, but may be work as a safety net. */ | |
3716 if (cplpl && fvdef == flistseen) | |
3717 { | |
3718 make_C_tag (TRUE); /* a function */ | |
3719 fvdef = fignore; | |
3720 break; | |
3721 } | |
428 | 3722 break; |
3723 case ';': | |
2325 | 3724 if (definedef != dnone || inattribute) |
428 | 3725 break; |
458 | 3726 switch (typdef) |
428 | 3727 { |
458 | 3728 case tend: |
3729 case ttypeseen: | |
3730 make_C_tag (FALSE); /* a typedef */ | |
3731 typdef = tnone; | |
3732 fvdef = fvnone; | |
428 | 3733 break; |
458 | 3734 case tnone: |
3735 case tinbody: | |
3736 case tignore: | |
3737 switch (fvdef) | |
3738 { | |
3739 case fignore: | |
2225 | 3740 if (typdef == tignore || cplpl) |
458 | 3741 fvdef = fvnone; |
3742 break; | |
3743 case fvnameseen: | |
2325 | 3744 if ((globals && bracelev == 0 && (!fvextern || declarations)) |
458 | 3745 || (members && instruct)) |
3746 make_C_tag (FALSE); /* a variable */ | |
3747 fvextern = FALSE; | |
3748 fvdef = fvnone; | |
3749 token.valid = FALSE; | |
3750 break; | |
3751 case flistseen: | |
2325 | 3752 if ((declarations |
3753 && (cplpl || !instruct) | |
3754 && (typdef == tnone || (typdef != tignore && instruct))) | |
3755 || (members | |
3756 && plainc && instruct)) | |
3757 make_C_tag (TRUE); /* a function */ | |
458 | 3758 /* FALLTHRU */ |
3759 default: | |
3760 fvextern = FALSE; | |
3761 fvdef = fvnone; | |
3762 if (declarations | |
2225 | 3763 && cplpl && structdef == stagseen) |
458 | 3764 make_C_tag (FALSE); /* forward declaration */ |
3765 else | |
3766 token.valid = FALSE; | |
3767 } /* switch (fvdef) */ | |
428 | 3768 /* FALLTHRU */ |
3769 default: | |
458 | 3770 if (!instruct) |
3771 typdef = tnone; | |
428 | 3772 } |
3773 if (structdef == stagseen) | |
3774 structdef = snone; | |
3775 break; | |
3776 case ',': | |
2325 | 3777 if (definedef != dnone || inattribute) |
428 | 3778 break; |
3779 switch (objdef) | |
3780 { | |
3781 case omethodtag: | |
3782 case omethodparm: | |
3783 make_C_tag (TRUE); /* an Objective C method */ | |
3784 objdef = oinbody; | |
3785 break; | |
3786 } | |
3787 switch (fvdef) | |
3788 { | |
458 | 3789 case fdefunkey: |
428 | 3790 case foperator: |
458 | 3791 case fstartlist: |
428 | 3792 case finlist: |
3793 case fignore: | |
3794 case vignore: | |
3795 break; | |
458 | 3796 case fdefunname: |
3797 fvdef = fignore; | |
3798 break; | |
2325 | 3799 case fvnameseen: |
3800 if (parlev == 0 | |
3801 && ((globals | |
3802 && bracelev == 0 | |
3803 && templatelev == 0 | |
3804 && (!fvextern || declarations)) | |
3805 || (members && instruct))) | |
3806 make_C_tag (FALSE); /* a variable */ | |
458 | 3807 break; |
2325 | 3808 case flistseen: |
458 | 3809 if ((declarations && typdef == tnone && !instruct) |
3810 || (members && typdef != tignore && instruct)) | |
3811 { | |
2325 | 3812 make_C_tag (TRUE); /* a function */ |
458 | 3813 fvdef = fvnameseen; |
3814 } | |
3815 else if (!declarations) | |
3816 fvdef = fvnone; | |
3817 token.valid = FALSE; | |
428 | 3818 break; |
3819 default: | |
3820 fvdef = fvnone; | |
3821 } | |
3822 if (structdef == stagseen) | |
3823 structdef = snone; | |
3824 break; | |
2325 | 3825 case ']': |
3826 if (definedef != dnone || inattribute) | |
428 | 3827 break; |
458 | 3828 if (structdef == stagseen) |
3829 structdef = snone; | |
3830 switch (typdef) | |
428 | 3831 { |
458 | 3832 case ttypeseen: |
3833 case tend: | |
428 | 3834 typdef = tignore; |
3835 make_C_tag (FALSE); /* a typedef */ | |
3836 break; | |
458 | 3837 case tnone: |
3838 case tinbody: | |
3839 switch (fvdef) | |
3840 { | |
3841 case foperator: | |
3842 case finlist: | |
3843 case fignore: | |
3844 case vignore: | |
3845 break; | |
3846 case fvnameseen: | |
2325 | 3847 if ((members && bracelev == 1) |
3848 || (globals && bracelev == 0 | |
458 | 3849 && (!fvextern || declarations))) |
3850 make_C_tag (FALSE); /* a variable */ | |
3851 /* FALLTHRU */ | |
3852 default: | |
3853 fvdef = fvnone; | |
3854 } | |
428 | 3855 break; |
3856 } | |
3857 break; | |
3858 case '(': | |
2325 | 3859 if (inattribute) |
3860 { | |
3861 attrparlev++; | |
3862 break; | |
3863 } | |
428 | 3864 if (definedef != dnone) |
3865 break; | |
3866 if (objdef == otagseen && parlev == 0) | |
3867 objdef = oparenseen; | |
3868 switch (fvdef) | |
3869 { | |
3870 case fvnameseen: | |
3871 if (typdef == ttypeseen | |
3872 && *lp != '*' | |
458 | 3873 && !instruct) |
428 | 3874 { |
3875 /* This handles constructs like: | |
3876 typedef void OperatorFun (int fun); */ | |
3877 make_C_tag (FALSE); | |
3878 typdef = tignore; | |
458 | 3879 fvdef = fignore; |
3880 break; | |
428 | 3881 } |
3882 /* FALLTHRU */ | |
3883 case foperator: | |
3884 fvdef = fstartlist; | |
3885 break; | |
3886 case flistseen: | |
3887 fvdef = finlist; | |
3888 break; | |
3889 } | |
3890 parlev++; | |
3891 break; | |
3892 case ')': | |
2325 | 3893 if (inattribute) |
3894 { | |
3895 if (--attrparlev == 0) | |
3896 inattribute = FALSE; | |
3897 break; | |
3898 } | |
428 | 3899 if (definedef != dnone) |
3900 break; | |
3901 if (objdef == ocatseen && parlev == 1) | |
3902 { | |
3903 make_C_tag (TRUE); /* an Objective C category */ | |
3904 objdef = oignore; | |
3905 } | |
3906 if (--parlev == 0) | |
3907 { | |
3908 switch (fvdef) | |
3909 { | |
3910 case fstartlist: | |
3911 case finlist: | |
3912 fvdef = flistseen; | |
3913 break; | |
3914 } | |
458 | 3915 if (!instruct |
3916 && (typdef == tend | |
3917 || typdef == ttypeseen)) | |
428 | 3918 { |
3919 typdef = tignore; | |
3920 make_C_tag (FALSE); /* a typedef */ | |
3921 } | |
3922 } | |
3923 else if (parlev < 0) /* can happen due to ill-conceived #if's. */ | |
3924 parlev = 0; | |
3925 break; | |
3926 case '{': | |
3927 if (definedef != dnone) | |
3928 break; | |
3929 if (typdef == ttypeseen) | |
3930 { | |
531 | 3931 /* Whenever typdef is set to tinbody (currently only |
2325 | 3932 here), typdefbracelev should be set to bracelev. */ |
531 | 3933 typdef = tinbody; |
2325 | 3934 typdefbracelev = bracelev; |
428 | 3935 } |
3936 switch (fvdef) | |
3937 { | |
3938 case flistseen: | |
458 | 3939 make_C_tag (TRUE); /* a function */ |
428 | 3940 /* FALLTHRU */ |
3941 case fignore: | |
3942 fvdef = fvnone; | |
3943 break; | |
3944 case fvnone: | |
3945 switch (objdef) | |
3946 { | |
3947 case otagseen: | |
3948 make_C_tag (TRUE); /* an Objective C class */ | |
3949 objdef = oignore; | |
3950 break; | |
3951 case omethodtag: | |
3952 case omethodparm: | |
3953 make_C_tag (TRUE); /* an Objective C method */ | |
3954 objdef = oinbody; | |
3955 break; | |
3956 default: | |
3957 /* Neutralize `extern "C" {' grot. */ | |
2325 | 3958 if (bracelev == 0 && structdef == snone && nestlev == 0 |
458 | 3959 && typdef == tnone) |
2325 | 3960 bracelev = -1; |
428 | 3961 } |
2225 | 3962 break; |
428 | 3963 } |
458 | 3964 switch (structdef) |
3965 { | |
3966 case skeyseen: /* unnamed struct */ | |
2325 | 3967 pushclass_above (bracelev, NULL, 0); |
458 | 3968 structdef = snone; |
3969 break; | |
3970 case stagseen: /* named struct or enum */ | |
3971 case scolonseen: /* a class */ | |
2325 | 3972 pushclass_above (bracelev,token.line+token.offset, token.length); |
458 | 3973 structdef = snone; |
3974 make_C_tag (FALSE); /* a struct or enum */ | |
3975 break; | |
3976 } | |
2325 | 3977 bracelev++; |
428 | 3978 break; |
3979 case '*': | |
3980 if (definedef != dnone) | |
3981 break; | |
3982 if (fvdef == fstartlist) | |
2225 | 3983 { |
3984 fvdef = fvnone; /* avoid tagging `foo' in `foo (*bar()) ()' */ | |
3985 token.valid = FALSE; | |
3986 } | |
428 | 3987 break; |
3988 case '}': | |
3989 if (definedef != dnone) | |
3990 break; | |
2225 | 3991 if (!ignoreindent && lp == newlb.buffer + 1) |
428 | 3992 { |
2325 | 3993 if (bracelev != 0) |
2225 | 3994 token.valid = FALSE; |
2325 | 3995 bracelev = 0; /* reset brace level if first column */ |
428 | 3996 parlev = 0; /* also reset paren level, just in case... */ |
3997 } | |
2325 | 3998 else if (bracelev > 0) |
3999 bracelev--; | |
2225 | 4000 else |
4001 token.valid = FALSE; /* something gone amiss, token unreliable */ | |
2325 | 4002 popclass_above (bracelev); |
458 | 4003 structdef = snone; |
2325 | 4004 /* Only if typdef == tinbody is typdefbracelev significant. */ |
4005 if (typdef == tinbody && bracelev <= typdefbracelev) | |
428 | 4006 { |
2325 | 4007 assert (bracelev == typdefbracelev); |
458 | 4008 typdef = tend; |
428 | 4009 } |
4010 break; | |
4011 case '=': | |
4012 if (definedef != dnone) | |
4013 break; | |
4014 switch (fvdef) | |
4015 { | |
4016 case foperator: | |
4017 case finlist: | |
4018 case fignore: | |
4019 case vignore: | |
4020 break; | |
4021 case fvnameseen: | |
2325 | 4022 if ((members && bracelev == 1) |
4023 || (globals && bracelev == 0 && (!fvextern || declarations))) | |
428 | 4024 make_C_tag (FALSE); /* a variable */ |
4025 /* FALLTHRU */ | |
4026 default: | |
4027 fvdef = vignore; | |
4028 } | |
4029 break; | |
458 | 4030 case '<': |
2325 | 4031 if (cplpl |
4032 && (structdef == stagseen || fvdef == fvnameseen)) | |
458 | 4033 { |
2325 | 4034 templatelev++; |
458 | 4035 break; |
4036 } | |
4037 goto resetfvdef; | |
4038 case '>': | |
2325 | 4039 if (templatelev > 0) |
458 | 4040 { |
2325 | 4041 templatelev--; |
458 | 4042 break; |
4043 } | |
4044 goto resetfvdef; | |
428 | 4045 case '+': |
4046 case '-': | |
2325 | 4047 if (objdef == oinbody && bracelev == 0) |
428 | 4048 { |
4049 objdef = omethodsign; | |
4050 break; | |
4051 } | |
4052 /* FALLTHRU */ | |
458 | 4053 resetfvdef: |
2325 | 4054 case '#': case '~': case '&': case '%': case '/': |
4055 case '|': case '^': case '!': case '.': case '?': | |
428 | 4056 if (definedef != dnone) |
4057 break; | |
4058 /* These surely cannot follow a function tag in C. */ | |
4059 switch (fvdef) | |
4060 { | |
4061 case foperator: | |
4062 case finlist: | |
4063 case fignore: | |
4064 case vignore: | |
4065 break; | |
4066 default: | |
4067 fvdef = fvnone; | |
4068 } | |
4069 break; | |
4070 case '\0': | |
4071 if (objdef == otagseen) | |
4072 { | |
4073 make_C_tag (TRUE); /* an Objective C class */ | |
4074 objdef = oignore; | |
4075 } | |
4076 /* If a macro spans multiple lines don't reset its state. */ | |
4077 if (quotednl) | |
4078 CNL_SAVE_DEFINEDEF (); | |
4079 else | |
4080 CNL (); | |
4081 break; | |
4082 } /* switch (c) */ | |
4083 | |
4084 } /* while not eof */ | |
458 | 4085 |
4086 free (lbs[0].lb.buffer); | |
4087 free (lbs[1].lb.buffer); | |
428 | 4088 } |
4089 | |
4090 /* | |
4091 * Process either a C++ file or a C file depending on the setting | |
4092 * of a global flag. | |
4093 */ | |
442 | 4094 static void |
428 | 4095 default_C_entries (inf) |
4096 FILE *inf; | |
4097 { | |
458 | 4098 C_entries (cplusplus ? C_PLPL : C_AUTO, inf); |
428 | 4099 } |
4100 | |
458 | 4101 /* Always do plain C. */ |
442 | 4102 static void |
428 | 4103 plain_C_entries (inf) |
4104 FILE *inf; | |
4105 { | |
4106 C_entries (0, inf); | |
4107 } | |
4108 | |
4109 /* Always do C++. */ | |
442 | 4110 static void |
428 | 4111 Cplusplus_entries (inf) |
4112 FILE *inf; | |
4113 { | |
4114 C_entries (C_PLPL, inf); | |
4115 } | |
4116 | |
4117 /* Always do Java. */ | |
442 | 4118 static void |
428 | 4119 Cjava_entries (inf) |
4120 FILE *inf; | |
4121 { | |
4122 C_entries (C_JAVA, inf); | |
4123 } | |
4124 | |
4125 /* Always do C*. */ | |
442 | 4126 static void |
428 | 4127 Cstar_entries (inf) |
4128 FILE *inf; | |
4129 { | |
4130 C_entries (C_STAR, inf); | |
4131 } | |
4132 | |
4133 /* Always do Yacc. */ | |
442 | 4134 static void |
428 | 4135 Yacc_entries (inf) |
4136 FILE *inf; | |
4137 { | |
4138 C_entries (YACC, inf); | |
4139 } | |
458 | 4140 |
428 | 4141 |
709 | 4142 /* Useful macros. */ |
428 | 4143 #define LOOP_ON_INPUT_LINES(file_pointer, line_buffer, char_pointer) \ |
2225 | 4144 for (; /* loop initialization */ \ |
428 | 4145 !feof (file_pointer) /* loop test */ \ |
2225 | 4146 && /* instructions at start of loop */ \ |
4147 (readline (&line_buffer, file_pointer), \ | |
4148 char_pointer = line_buffer.buffer, \ | |
428 | 4149 TRUE); \ |
4150 ) | |
2554 | 4151 |
4152 #define LOOKING_AT(cp, kw) /* kw is the keyword, a literal string */ \ | |
4153 ((assert("" kw), TRUE) /* syntax error if not a literal string */ \ | |
4154 && strneq ((cp), kw, sizeof(kw)-1) /* cp points at kw */ \ | |
4155 && notinname ((cp)[sizeof(kw)-1]) /* end of kw */ \ | |
4156 && ((cp) = skip_spaces((cp)+sizeof(kw)-1))) /* skip spaces */ | |
4157 | |
4158 /* Similar to LOOKING_AT but does not use notinname, does not skip */ | |
4159 #define LOOKING_AT_NOCASE(cp, kw) /* the keyword is a literal string */ \ | |
4160 ((assert("" kw), TRUE) /* syntax error if not a literal string */ \ | |
4161 && strncaseeq ((cp), kw, sizeof(kw)-1) /* cp points at kw */ \ | |
4162 && ((cp) += sizeof(kw)-1)) /* skip spaces */ | |
428 | 4163 |
4164 /* | |
4165 * Read a file, but do no processing. This is used to do regexp | |
4166 * matching on files that have no language defined. | |
4167 */ | |
442 | 4168 static void |
428 | 4169 just_read_file (inf) |
4170 FILE *inf; | |
4171 { | |
4172 register char *dummy; | |
4173 | |
4174 LOOP_ON_INPUT_LINES (inf, lb, dummy) | |
4175 continue; | |
4176 } | |
458 | 4177 |
428 | 4178 |
4179 /* Fortran parsing */ | |
4180 | |
2225 | 4181 static void F_takeprec __P((void)); |
4182 static void F_getit __P((FILE *)); | |
428 | 4183 |
442 | 4184 static void |
2225 | 4185 F_takeprec () |
428 | 4186 { |
4187 dbp = skip_spaces (dbp); | |
4188 if (*dbp != '*') | |
4189 return; | |
4190 dbp++; | |
4191 dbp = skip_spaces (dbp); | |
4192 if (strneq (dbp, "(*)", 3)) | |
4193 { | |
4194 dbp += 3; | |
4195 return; | |
4196 } | |
458 | 4197 if (!ISDIGIT (*dbp)) |
428 | 4198 { |
4199 --dbp; /* force failure */ | |
4200 return; | |
4201 } | |
4202 do | |
4203 dbp++; | |
458 | 4204 while (ISDIGIT (*dbp)); |
428 | 4205 } |
4206 | |
4207 static void | |
2225 | 4208 F_getit (inf) |
428 | 4209 FILE *inf; |
4210 { | |
4211 register char *cp; | |
4212 | |
4213 dbp = skip_spaces (dbp); | |
4214 if (*dbp == '\0') | |
4215 { | |
2225 | 4216 readline (&lb, inf); |
428 | 4217 dbp = lb.buffer; |
4218 if (dbp[5] != '&') | |
4219 return; | |
4220 dbp += 6; | |
4221 dbp = skip_spaces (dbp); | |
4222 } | |
458 | 4223 if (!ISALPHA (*dbp) && *dbp != '_' && *dbp != '$') |
428 | 4224 return; |
4225 for (cp = dbp + 1; *cp != '\0' && intoken (*cp); cp++) | |
4226 continue; | |
2225 | 4227 make_tag (dbp, cp-dbp, TRUE, |
4228 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
428 | 4229 } |
4230 | |
4231 | |
442 | 4232 static void |
428 | 4233 Fortran_functions (inf) |
4234 FILE *inf; | |
4235 { | |
4236 LOOP_ON_INPUT_LINES (inf, lb, dbp) | |
4237 { | |
4238 if (*dbp == '%') | |
4239 dbp++; /* Ratfor escape to fortran */ | |
4240 dbp = skip_spaces (dbp); | |
4241 if (*dbp == '\0') | |
4242 continue; | |
4243 switch (lowcase (*dbp)) | |
4244 { | |
4245 case 'i': | |
2225 | 4246 if (nocase_tail ("integer")) |
4247 F_takeprec (); | |
428 | 4248 break; |
4249 case 'r': | |
2225 | 4250 if (nocase_tail ("real")) |
4251 F_takeprec (); | |
428 | 4252 break; |
4253 case 'l': | |
2225 | 4254 if (nocase_tail ("logical")) |
4255 F_takeprec (); | |
428 | 4256 break; |
4257 case 'c': | |
2225 | 4258 if (nocase_tail ("complex") || nocase_tail ("character")) |
4259 F_takeprec (); | |
428 | 4260 break; |
4261 case 'd': | |
2225 | 4262 if (nocase_tail ("double")) |
428 | 4263 { |
4264 dbp = skip_spaces (dbp); | |
4265 if (*dbp == '\0') | |
4266 continue; | |
2225 | 4267 if (nocase_tail ("precision")) |
428 | 4268 break; |
4269 continue; | |
4270 } | |
4271 break; | |
4272 } | |
4273 dbp = skip_spaces (dbp); | |
4274 if (*dbp == '\0') | |
4275 continue; | |
4276 switch (lowcase (*dbp)) | |
4277 { | |
4278 case 'f': | |
2225 | 4279 if (nocase_tail ("function")) |
4280 F_getit (inf); | |
428 | 4281 continue; |
4282 case 's': | |
2225 | 4283 if (nocase_tail ("subroutine")) |
4284 F_getit (inf); | |
428 | 4285 continue; |
4286 case 'e': | |
2225 | 4287 if (nocase_tail ("entry")) |
4288 F_getit (inf); | |
428 | 4289 continue; |
4290 case 'b': | |
2225 | 4291 if (nocase_tail ("blockdata") || nocase_tail ("block data")) |
428 | 4292 { |
4293 dbp = skip_spaces (dbp); | |
4294 if (*dbp == '\0') /* assume un-named */ | |
2225 | 4295 make_tag ("blockdata", 9, TRUE, |
4296 lb.buffer, dbp - lb.buffer, lineno, linecharno); | |
428 | 4297 else |
2225 | 4298 F_getit (inf); /* look for name */ |
428 | 4299 } |
4300 continue; | |
4301 } | |
4302 } | |
4303 } | |
458 | 4304 |
428 | 4305 |
4306 /* | |
4307 * Ada parsing | |
2225 | 4308 * Original code by |
4309 * Philippe Waroquiers (1998) | |
428 | 4310 */ |
442 | 4311 |
2225 | 4312 static void Ada_getit __P((FILE *, char *)); |
442 | 4313 |
428 | 4314 /* Once we are positioned after an "interesting" keyword, let's get |
4315 the real tag value necessary. */ | |
4316 static void | |
2225 | 4317 Ada_getit (inf, name_qualifier) |
428 | 4318 FILE *inf; |
4319 char *name_qualifier; | |
4320 { | |
4321 register char *cp; | |
4322 char *name; | |
4323 char c; | |
4324 | |
4325 while (!feof (inf)) | |
4326 { | |
4327 dbp = skip_spaces (dbp); | |
4328 if (*dbp == '\0' | |
4329 || (dbp[0] == '-' && dbp[1] == '-')) | |
4330 { | |
2225 | 4331 readline (&lb, inf); |
428 | 4332 dbp = lb.buffer; |
4333 } | |
2225 | 4334 switch (lowcase(*dbp)) |
428 | 4335 { |
4336 case 'b': | |
2225 | 4337 if (nocase_tail ("body")) |
428 | 4338 { |
4339 /* Skipping body of procedure body or package body or .... | |
4340 resetting qualifier to body instead of spec. */ | |
4341 name_qualifier = "/b"; | |
4342 continue; | |
4343 } | |
4344 break; | |
4345 case 't': | |
4346 /* Skipping type of task type or protected type ... */ | |
2225 | 4347 if (nocase_tail ("type")) |
428 | 4348 continue; |
4349 break; | |
4350 } | |
4351 if (*dbp == '"') | |
4352 { | |
4353 dbp += 1; | |
4354 for (cp = dbp; *cp != '\0' && *cp != '"'; cp++) | |
4355 continue; | |
4356 } | |
4357 else | |
4358 { | |
4359 dbp = skip_spaces (dbp); | |
4360 for (cp = dbp; | |
4361 (*cp != '\0' | |
458 | 4362 && (ISALPHA (*cp) || ISDIGIT (*cp) || *cp == '_' || *cp == '.')); |
428 | 4363 cp++) |
4364 continue; | |
4365 if (cp == dbp) | |
4366 return; | |
4367 } | |
4368 c = *cp; | |
4369 *cp = '\0'; | |
4370 name = concat (dbp, name_qualifier, ""); | |
4371 *cp = c; | |
2225 | 4372 make_tag (name, strlen (name), TRUE, |
4373 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
4374 free (name); | |
428 | 4375 if (c == '"') |
4376 dbp = cp + 1; | |
4377 return; | |
4378 } | |
4379 } | |
4380 | |
442 | 4381 static void |
428 | 4382 Ada_funcs (inf) |
4383 FILE *inf; | |
4384 { | |
4385 bool inquote = FALSE; | |
2225 | 4386 bool skip_till_semicolumn = FALSE; |
428 | 4387 |
4388 LOOP_ON_INPUT_LINES (inf, lb, dbp) | |
4389 { | |
4390 while (*dbp != '\0') | |
4391 { | |
4392 /* Skip a string i.e. "abcd". */ | |
4393 if (inquote || (*dbp == '"')) | |
4394 { | |
4395 dbp = etags_strchr ((inquote) ? dbp : dbp+1, '"'); | |
4396 if (dbp != NULL) | |
4397 { | |
4398 inquote = FALSE; | |
4399 dbp += 1; | |
4400 continue; /* advance char */ | |
4401 } | |
4402 else | |
4403 { | |
4404 inquote = TRUE; | |
4405 break; /* advance line */ | |
4406 } | |
4407 } | |
4408 | |
4409 /* Skip comments. */ | |
4410 if (dbp[0] == '-' && dbp[1] == '-') | |
4411 break; /* advance line */ | |
4412 | |
4413 /* Skip character enclosed in single quote i.e. 'a' | |
4414 and skip single quote starting an attribute i.e. 'Image. */ | |
4415 if (*dbp == '\'') | |
4416 { | |
4417 dbp++ ; | |
4418 if (*dbp != '\0') | |
4419 dbp++; | |
4420 continue; | |
4421 } | |
4422 | |
2225 | 4423 if (skip_till_semicolumn) |
4424 { | |
4425 if (*dbp == ';') | |
4426 skip_till_semicolumn = FALSE; | |
4427 dbp++; | |
4428 continue; /* advance char */ | |
4429 } | |
4430 | |
428 | 4431 /* Search for beginning of a token. */ |
4432 if (!begtoken (*dbp)) | |
4433 { | |
4434 dbp++; | |
4435 continue; /* advance char */ | |
4436 } | |
4437 | |
4438 /* We are at the beginning of a token. */ | |
2225 | 4439 switch (lowcase(*dbp)) |
428 | 4440 { |
4441 case 'f': | |
2225 | 4442 if (!packages_only && nocase_tail ("function")) |
4443 Ada_getit (inf, "/f"); | |
428 | 4444 else |
4445 break; /* from switch */ | |
4446 continue; /* advance char */ | |
4447 case 'p': | |
2225 | 4448 if (!packages_only && nocase_tail ("procedure")) |
4449 Ada_getit (inf, "/p"); | |
4450 else if (nocase_tail ("package")) | |
4451 Ada_getit (inf, "/s"); | |
4452 else if (nocase_tail ("protected")) /* protected type */ | |
4453 Ada_getit (inf, "/t"); | |
428 | 4454 else |
4455 break; /* from switch */ | |
4456 continue; /* advance char */ | |
2225 | 4457 |
4458 case 'u': | |
4459 if (typedefs && !packages_only && nocase_tail ("use")) | |
4460 { | |
4461 /* when tagging types, avoid tagging use type Pack.Typename; | |
4462 for this, we will skip everything till a ; */ | |
4463 skip_till_semicolumn = TRUE; | |
4464 continue; /* advance char */ | |
4465 } | |
4466 | |
428 | 4467 case 't': |
2225 | 4468 if (!packages_only && nocase_tail ("task")) |
4469 Ada_getit (inf, "/k"); | |
4470 else if (typedefs && !packages_only && nocase_tail ("type")) | |
428 | 4471 { |
2225 | 4472 Ada_getit (inf, "/t"); |
428 | 4473 while (*dbp != '\0') |
4474 dbp += 1; | |
4475 } | |
4476 else | |
4477 break; /* from switch */ | |
4478 continue; /* advance char */ | |
4479 } | |
4480 | |
4481 /* Look for the end of the token. */ | |
4482 while (!endtoken (*dbp)) | |
4483 dbp++; | |
4484 | |
4485 } /* advance char */ | |
4486 } /* advance line */ | |
4487 } | |
458 | 4488 |
428 | 4489 |
4490 /* | |
4491 * Unix and microcontroller assembly tag handling | |
2225 | 4492 * Labels: /^[a-zA-Z_.$][a-zA_Z0-9_.$]*[: ^I^J]/ |
4493 * Idea by Bob Weiner, Motorola Inc. (1994) | |
428 | 4494 */ |
442 | 4495 static void |
428 | 4496 Asm_labels (inf) |
4497 FILE *inf; | |
4498 { | |
4499 register char *cp; | |
4500 | |
4501 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
4502 { | |
4503 /* If first char is alphabetic or one of [_.$], test for colon | |
4504 following identifier. */ | |
458 | 4505 if (ISALPHA (*cp) || *cp == '_' || *cp == '.' || *cp == '$') |
428 | 4506 { |
4507 /* Read past label. */ | |
4508 cp++; | |
458 | 4509 while (ISALNUM (*cp) || *cp == '_' || *cp == '.' || *cp == '$') |
428 | 4510 cp++; |
442 | 4511 if (*cp == ':' || iswhite (*cp)) |
2225 | 4512 /* Found end of label, so copy it and add it to the table. */ |
4513 make_tag (lb.buffer, cp - lb.buffer, TRUE, | |
428 | 4514 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); |
4515 } | |
4516 } | |
4517 } | |
458 | 4518 |
428 | 4519 |
4520 /* | |
458 | 4521 * Perl support |
2225 | 4522 * Perl sub names: /^sub[ \t\n]+[^ \t\n{]+/ |
428 | 4523 * Perl variable names: /^(my|local).../ |
2225 | 4524 * Original code by Bart Robinson <lomew@cs.utah.edu> (1995) |
4525 * Additions by Michael Ernst <mernst@alum.mit.edu> (1997) | |
4526 * Ideas by Kai Großjohann <Kai.Grossjohann@CS.Uni-Dortmund.DE> (2001) | |
428 | 4527 */ |
442 | 4528 static void |
428 | 4529 Perl_functions (inf) |
4530 FILE *inf; | |
4531 { | |
2225 | 4532 char *package = savestr ("main"); /* current package name */ |
428 | 4533 register char *cp; |
4534 | |
4535 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
4536 { | |
2225 | 4537 skip_spaces(cp); |
4538 | |
4539 if (LOOKING_AT (cp, "package")) | |
4540 { | |
4541 free (package); | |
4542 get_tag (cp, &package); | |
4543 } | |
4544 else if (LOOKING_AT (cp, "sub")) | |
428 | 4545 { |
2225 | 4546 char *pos; |
4547 char *sp = cp; | |
4548 | |
4549 while (!notinname (*cp)) | |
4550 cp++; | |
4551 if (cp == sp) | |
4552 continue; /* nothing found */ | |
4553 if ((pos = etags_strchr (sp, ':')) != NULL | |
4554 && pos < cp && pos[1] == ':') | |
4555 /* The name is already qualified. */ | |
4556 make_tag (sp, cp - sp, TRUE, | |
4557 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
4558 else | |
4559 /* Qualify it. */ | |
4560 { | |
4561 char savechar, *name; | |
4562 | |
4563 savechar = *cp; | |
4564 *cp = '\0'; | |
4565 name = concat (package, "::", sp); | |
4566 *cp = savechar; | |
4567 make_tag (name, strlen(name), TRUE, | |
4568 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
4569 free (name); | |
4570 } | |
428 | 4571 } |
2225 | 4572 else if (globals) /* only if we are tagging global vars */ |
428 | 4573 { |
2225 | 4574 /* Skip a qualifier, if any. */ |
4575 bool qual = LOOKING_AT (cp, "my") || LOOKING_AT (cp, "local"); | |
428 | 4576 /* After "my" or "local", but before any following paren or space. */ |
2225 | 4577 char *varstart = cp; |
4578 | |
4579 if (qual /* should this be removed? If yes, how? */ | |
4580 && (*cp == '$' || *cp == '@' || *cp == '%')) | |
428 | 4581 { |
2225 | 4582 varstart += 1; |
4583 do | |
428 | 4584 cp++; |
2225 | 4585 while (ISALNUM (*cp) || *cp == '_'); |
428 | 4586 } |
2225 | 4587 else if (qual) |
428 | 4588 { |
4589 /* Should be examining a variable list at this point; | |
4590 could insist on seeing an open parenthesis. */ | |
4591 while (*cp != '\0' && *cp != ';' && *cp != '=' && *cp != ')') | |
4592 cp++; | |
4593 } | |
2225 | 4594 else |
4595 continue; | |
4596 | |
4597 make_tag (varstart, cp - varstart, FALSE, | |
4598 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
428 | 4599 } |
4600 } | |
3517 | 4601 free (package); |
428 | 4602 } |
458 | 4603 |
709 | 4604 |
428 | 4605 /* |
458 | 4606 * Python support |
2225 | 4607 * Look for /^[\t]*def[ \t\n]+[^ \t\n(:]+/ or /^class[ \t\n]+[^ \t\n(:]+/ |
4608 * Idea by Eric S. Raymond <esr@thyrsus.com> (1997) | |
4609 * More ideas by seb bacon <seb@jamkit.com> (2002) | |
428 | 4610 */ |
442 | 4611 static void |
428 | 4612 Python_functions (inf) |
4613 FILE *inf; | |
4614 { | |
4615 register char *cp; | |
4616 | |
4617 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
2225 | 4618 { |
4619 cp = skip_spaces (cp); | |
4620 if (LOOKING_AT (cp, "def") || LOOKING_AT (cp, "class")) | |
4621 { | |
4622 char *name = cp; | |
4623 while (!notinname (*cp) && *cp != ':') | |
4624 cp++; | |
4625 make_tag (name, cp - name, TRUE, | |
4626 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
4627 } | |
4628 } | |
709 | 4629 } |
4630 | |
4631 | |
4632 /* | |
4633 * PHP support | |
4634 * Look for: | |
4635 * - /^[ \t]*function[ \t\n]+[^ \t\n(]+/ | |
4636 * - /^[ \t]*class[ \t\n]+[^ \t\n]+/ | |
4637 * - /^[ \t]*define\(\"[^\"]+/ | |
4638 * Only with --members: | |
4639 * - /^[ \t]*var[ \t\n]+\$[^ \t\n=;]/ | |
2225 | 4640 * Idea by Diez B. Roggisch (2001) |
709 | 4641 */ |
4642 static void | |
4643 PHP_functions (inf) | |
4644 FILE *inf; | |
4645 { | |
2225 | 4646 register char *cp, *name; |
709 | 4647 bool search_identifier = FALSE; |
4648 | |
4649 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
428 | 4650 { |
709 | 4651 cp = skip_spaces (cp); |
2225 | 4652 name = cp; |
709 | 4653 if (search_identifier |
4654 && *cp != '\0') | |
428 | 4655 { |
2225 | 4656 while (!notinname (*cp)) |
428 | 4657 cp++; |
2225 | 4658 make_tag (name, cp - name, TRUE, |
4659 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
709 | 4660 search_identifier = FALSE; |
428 | 4661 } |
709 | 4662 else if (LOOKING_AT (cp, "function")) |
4663 { | |
4664 if(*cp == '&') | |
4665 cp = skip_spaces (cp+1); | |
4666 if(*cp != '\0') | |
4667 { | |
2225 | 4668 name = cp; |
4669 while (!notinname (*cp)) | |
709 | 4670 cp++; |
2225 | 4671 make_tag (name, cp - name, TRUE, |
4672 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
709 | 4673 } |
4674 else | |
4675 search_identifier = TRUE; | |
4676 } | |
4677 else if (LOOKING_AT (cp, "class")) | |
428 | 4678 { |
709 | 4679 if (*cp != '\0') |
4680 { | |
2225 | 4681 name = cp; |
709 | 4682 while (*cp != '\0' && !iswhite (*cp)) |
4683 cp++; | |
2225 | 4684 make_tag (name, cp - name, FALSE, |
4685 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
709 | 4686 } |
4687 else | |
4688 search_identifier = TRUE; | |
4689 } | |
4690 else if (strneq (cp, "define", 6) | |
4691 && (cp = skip_spaces (cp+6)) | |
4692 && *cp++ == '(' | |
4693 && (*cp == '"' || *cp == '\'')) | |
4694 { | |
4695 char quote = *cp++; | |
2225 | 4696 name = cp; |
709 | 4697 while (*cp != quote && *cp != '\0') |
428 | 4698 cp++; |
2225 | 4699 make_tag (name, cp - name, FALSE, |
4700 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
709 | 4701 } |
4702 else if (members | |
4703 && LOOKING_AT (cp, "var") | |
4704 && *cp == '$') | |
4705 { | |
2225 | 4706 name = cp; |
4707 while (!notinname(*cp)) | |
709 | 4708 cp++; |
2225 | 4709 make_tag (name, cp - name, FALSE, |
4710 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
428 | 4711 } |
4712 } | |
4713 } | |
458 | 4714 |
428 | 4715 |
2225 | 4716 /* |
428 | 4717 * Cobol tag functions |
4718 * We could look for anything that could be a paragraph name. | |
4719 * i.e. anything that starts in column 8 is one word and ends in a full stop. | |
2225 | 4720 * Idea by Corny de Souza (1993) |
428 | 4721 */ |
442 | 4722 static void |
428 | 4723 Cobol_paragraphs (inf) |
4724 FILE *inf; | |
4725 { | |
4726 register char *bp, *ep; | |
4727 | |
4728 LOOP_ON_INPUT_LINES (inf, lb, bp) | |
4729 { | |
4730 if (lb.len < 9) | |
4731 continue; | |
4732 bp += 8; | |
4733 | |
4734 /* If eoln, compiler option or comment ignore whole line. */ | |
458 | 4735 if (bp[-1] != ' ' || !ISALNUM (bp[0])) |
428 | 4736 continue; |
4737 | |
458 | 4738 for (ep = bp; ISALNUM (*ep) || *ep == '-'; ep++) |
428 | 4739 continue; |
4740 if (*ep++ == '.') | |
2225 | 4741 make_tag (bp, ep - bp, TRUE, |
4742 lb.buffer, ep - lb.buffer + 1, lineno, linecharno); | |
428 | 4743 } |
4744 } | |
458 | 4745 |
4746 | |
4747 /* | |
4748 * Makefile support | |
2225 | 4749 * Ideas by Assar Westerlund <assar@sics.se> (2001) |
458 | 4750 */ |
4751 static void | |
4752 Makefile_targets (inf) | |
4753 FILE *inf; | |
4754 { | |
4755 register char *bp; | |
4756 | |
4757 LOOP_ON_INPUT_LINES (inf, lb, bp) | |
4758 { | |
4759 if (*bp == '\t' || *bp == '#') | |
4760 continue; | |
4761 while (*bp != '\0' && *bp != '=' && *bp != ':') | |
4762 bp++; | |
2225 | 4763 if (*bp == ':' || (globals && *bp == '=')) |
3876 | 4764 { |
4765 /* We should detect if there is more than one tag, but we do not. | |
4766 We just skip initial and final spaces. */ | |
4767 char * namestart = skip_spaces (lb.buffer); | |
4768 while (--bp > namestart) | |
4769 if (!notinname (*bp)) | |
4770 break; | |
4771 make_tag (namestart, bp - namestart + 1, TRUE, | |
4772 lb.buffer, bp - lb.buffer + 2, lineno, linecharno); | |
4773 } | |
458 | 4774 } |
4775 } | |
4776 | |
428 | 4777 |
4778 /* | |
2225 | 4779 * Pascal parsing |
4780 * Original code by Mosur K. Mohan (1989) | |
4781 * | |
428 | 4782 * Locates tags for procedures & functions. Doesn't do any type- or |
4783 * var-definitions. It does look for the keyword "extern" or | |
4784 * "forward" immediately following the procedure statement; if found, | |
4785 * the tag is skipped. | |
4786 */ | |
442 | 4787 static void |
428 | 4788 Pascal_functions (inf) |
4789 FILE *inf; | |
4790 { | |
4791 linebuffer tline; /* mostly copied from C_entries */ | |
4792 long save_lcno; | |
2225 | 4793 int save_lineno, namelen, taglen; |
4794 char c, *name; | |
428 | 4795 |
4796 bool /* each of these flags is TRUE iff: */ | |
4797 incomment, /* point is inside a comment */ | |
4798 inquote, /* point is inside '..' string */ | |
4799 get_tagname, /* point is after PROCEDURE/FUNCTION | |
4800 keyword, so next item = potential tag */ | |
4801 found_tag, /* point is after a potential tag */ | |
4802 inparms, /* point is within parameter-list */ | |
4803 verify_tag; /* point has passed the parm-list, so the | |
4804 next token will determine whether this | |
4805 is a FORWARD/EXTERN to be ignored, or | |
4806 whether it is a real tag */ | |
4807 | |
2225 | 4808 save_lcno = save_lineno = namelen = taglen = 0; /* keep compiler quiet */ |
4809 name = NULL; /* keep compiler quiet */ | |
428 | 4810 dbp = lb.buffer; |
4811 *dbp = '\0'; | |
2225 | 4812 linebuffer_init (&tline); |
428 | 4813 |
4814 incomment = inquote = FALSE; | |
4815 found_tag = FALSE; /* have a proc name; check if extern */ | |
2225 | 4816 get_tagname = FALSE; /* found "procedure" keyword */ |
428 | 4817 inparms = FALSE; /* found '(' after "proc" */ |
4818 verify_tag = FALSE; /* check if "extern" is ahead */ | |
4819 | |
4820 | |
4821 while (!feof (inf)) /* long main loop to get next char */ | |
4822 { | |
4823 c = *dbp++; | |
4824 if (c == '\0') /* if end of line */ | |
4825 { | |
2225 | 4826 readline (&lb, inf); |
428 | 4827 dbp = lb.buffer; |
4828 if (*dbp == '\0') | |
4829 continue; | |
4830 if (!((found_tag && verify_tag) | |
4831 || get_tagname)) | |
4832 c = *dbp++; /* only if don't need *dbp pointing | |
4833 to the beginning of the name of | |
4834 the procedure or function */ | |
4835 } | |
4836 if (incomment) | |
4837 { | |
4838 if (c == '}') /* within { } comments */ | |
4839 incomment = FALSE; | |
4840 else if (c == '*' && *dbp == ')') /* within (* *) comments */ | |
4841 { | |
4842 dbp++; | |
4843 incomment = FALSE; | |
4844 } | |
4845 continue; | |
4846 } | |
4847 else if (inquote) | |
4848 { | |
4849 if (c == '\'') | |
4850 inquote = FALSE; | |
4851 continue; | |
4852 } | |
4853 else | |
4854 switch (c) | |
4855 { | |
4856 case '\'': | |
4857 inquote = TRUE; /* found first quote */ | |
4858 continue; | |
4859 case '{': /* found open { comment */ | |
4860 incomment = TRUE; | |
4861 continue; | |
4862 case '(': | |
4863 if (*dbp == '*') /* found open (* comment */ | |
4864 { | |
4865 incomment = TRUE; | |
4866 dbp++; | |
4867 } | |
4868 else if (found_tag) /* found '(' after tag, i.e., parm-list */ | |
4869 inparms = TRUE; | |
4870 continue; | |
4871 case ')': /* end of parms list */ | |
4872 if (inparms) | |
4873 inparms = FALSE; | |
4874 continue; | |
4875 case ';': | |
4876 if (found_tag && !inparms) /* end of proc or fn stmt */ | |
4877 { | |
4878 verify_tag = TRUE; | |
4879 break; | |
4880 } | |
4881 continue; | |
4882 } | |
4883 if (found_tag && verify_tag && (*dbp != ' ')) | |
4884 { | |
2225 | 4885 /* Check if this is an "extern" declaration. */ |
428 | 4886 if (*dbp == '\0') |
4887 continue; | |
4888 if (lowcase (*dbp == 'e')) | |
4889 { | |
2225 | 4890 if (nocase_tail ("extern")) /* superfluous, really! */ |
428 | 4891 { |
4892 found_tag = FALSE; | |
4893 verify_tag = FALSE; | |
4894 } | |
4895 } | |
4896 else if (lowcase (*dbp) == 'f') | |
4897 { | |
2225 | 4898 if (nocase_tail ("forward")) /* check for forward reference */ |
428 | 4899 { |
4900 found_tag = FALSE; | |
4901 verify_tag = FALSE; | |
4902 } | |
4903 } | |
4904 if (found_tag && verify_tag) /* not external proc, so make tag */ | |
4905 { | |
4906 found_tag = FALSE; | |
4907 verify_tag = FALSE; | |
2225 | 4908 make_tag (name, namelen, TRUE, |
4909 tline.buffer, taglen, save_lineno, save_lcno); | |
428 | 4910 continue; |
4911 } | |
4912 } | |
4913 if (get_tagname) /* grab name of proc or fn */ | |
4914 { | |
2225 | 4915 char *cp; |
4916 | |
428 | 4917 if (*dbp == '\0') |
4918 continue; | |
4919 | |
2225 | 4920 /* Find block name. */ |
4921 for (cp = dbp + 1; *cp != '\0' && !endtoken (*cp); cp++) | |
4922 continue; | |
4923 | |
4924 /* Save all values for later tagging. */ | |
458 | 4925 linebuffer_setlen (&tline, lb.len); |
428 | 4926 strcpy (tline.buffer, lb.buffer); |
4927 save_lineno = lineno; | |
4928 save_lcno = linecharno; | |
2225 | 4929 name = tline.buffer + (dbp - lb.buffer); |
4930 namelen = cp - dbp; | |
4931 taglen = cp - lb.buffer + 1; | |
4932 | |
428 | 4933 dbp = cp; /* set dbp to e-o-token */ |
4934 get_tagname = FALSE; | |
4935 found_tag = TRUE; | |
4936 continue; | |
4937 | |
2225 | 4938 /* And proceed to check for "extern". */ |
428 | 4939 } |
4940 else if (!incomment && !inquote && !found_tag) | |
4941 { | |
2225 | 4942 /* Check for proc/fn keywords. */ |
428 | 4943 switch (lowcase (c)) |
4944 { | |
4945 case 'p': | |
2225 | 4946 if (nocase_tail ("rocedure")) /* c = 'p', dbp has advanced */ |
428 | 4947 get_tagname = TRUE; |
4948 continue; | |
4949 case 'f': | |
2225 | 4950 if (nocase_tail ("unction")) |
428 | 4951 get_tagname = TRUE; |
4952 continue; | |
4953 } | |
4954 } | |
2225 | 4955 } /* while not eof */ |
428 | 4956 |
4957 free (tline.buffer); | |
4958 } | |
458 | 4959 |
428 | 4960 |
4961 /* | |
458 | 4962 * Lisp tag functions |
428 | 4963 * look for (def or (DEF, quote or QUOTE |
4964 */ | |
442 | 4965 |
709 | 4966 static void L_getit __P((void)); |
442 | 4967 |
428 | 4968 static void |
4969 L_getit () | |
4970 { | |
4971 if (*dbp == '\'') /* Skip prefix quote */ | |
4972 dbp++; | |
4973 else if (*dbp == '(') | |
4974 { | |
2225 | 4975 dbp++; |
4976 /* Try to skip "(quote " */ | |
4977 if (!LOOKING_AT (dbp, "quote") && !LOOKING_AT (dbp, "QUOTE")) | |
4978 /* Ok, then skip "(" before name in (defstruct (foo)) */ | |
4979 dbp = skip_spaces (dbp); | |
428 | 4980 } |
2225 | 4981 get_tag (dbp, NULL); |
428 | 4982 } |
4983 | |
442 | 4984 static void |
428 | 4985 Lisp_functions (inf) |
4986 FILE *inf; | |
4987 { | |
4988 LOOP_ON_INPUT_LINES (inf, lb, dbp) | |
4989 { | |
2225 | 4990 if (dbp[0] != '(') |
4991 continue; | |
4992 | |
4993 if (strneq (dbp+1, "def", 3) || strneq (dbp+1, "DEF", 3)) | |
428 | 4994 { |
2225 | 4995 dbp = skip_non_spaces (dbp); |
4996 dbp = skip_spaces (dbp); | |
4997 L_getit (); | |
4998 } | |
4999 else | |
5000 { | |
5001 /* Check for (foo::defmumble name-defined ... */ | |
5002 do | |
5003 dbp++; | |
5004 while (!notinname (*dbp) && *dbp != ':'); | |
5005 if (*dbp == ':') | |
428 | 5006 { |
5007 do | |
5008 dbp++; | |
2225 | 5009 while (*dbp == ':'); |
5010 | |
5011 if (strneq (dbp, "def", 3) || strneq (dbp, "DEF", 3)) | |
428 | 5012 { |
2225 | 5013 dbp = skip_non_spaces (dbp); |
5014 dbp = skip_spaces (dbp); | |
5015 L_getit (); | |
428 | 5016 } |
5017 } | |
5018 } | |
5019 } | |
5020 } | |
458 | 5021 |
428 | 5022 |
5023 /* | |
2325 | 5024 * Lua script language parsing |
5025 * Original code by David A. Capello <dacap@users.sourceforge.net> (2004) | |
5026 * | |
5027 * "function" and "local function" are tags if they start at column 1. | |
5028 */ | |
5029 static void | |
5030 Lua_functions (inf) | |
5031 FILE *inf; | |
5032 { | |
5033 register char *bp; | |
5034 | |
5035 LOOP_ON_INPUT_LINES (inf, lb, bp) | |
5036 { | |
5037 if (bp[0] != 'f' && bp[0] != 'l') | |
5038 continue; | |
5039 | |
3876 | 5040 (void)LOOKING_AT (bp, "local"); /* skip possible "local" */ |
2325 | 5041 |
5042 if (LOOKING_AT (bp, "function")) | |
5043 get_tag (bp, NULL); | |
5044 } | |
5045 } | |
5046 | |
5047 | |
5048 /* | |
2554 | 5049 * Postscript tags |
428 | 5050 * Just look for lines where the first character is '/' |
5051 * Also look at "defineps" for PSWrap | |
2225 | 5052 * Ideas by: |
5053 * Richard Mlynarik <mly@adoc.xerox.com> (1997) | |
5054 * Masatake Yamato <masata-y@is.aist-nara.ac.jp> (1999) | |
428 | 5055 */ |
442 | 5056 static void |
2225 | 5057 PS_functions (inf) |
428 | 5058 FILE *inf; |
5059 { | |
5060 register char *bp, *ep; | |
5061 | |
5062 LOOP_ON_INPUT_LINES (inf, lb, bp) | |
5063 { | |
5064 if (bp[0] == '/') | |
5065 { | |
5066 for (ep = bp+1; | |
5067 *ep != '\0' && *ep != ' ' && *ep != '{'; | |
5068 ep++) | |
5069 continue; | |
2225 | 5070 make_tag (bp, ep - bp, TRUE, |
5071 lb.buffer, ep - lb.buffer + 1, lineno, linecharno); | |
428 | 5072 } |
2225 | 5073 else if (LOOKING_AT (bp, "defineps")) |
5074 get_tag (bp, NULL); | |
428 | 5075 } |
5076 } | |
5077 | |
5078 | |
5079 /* | |
2554 | 5080 * Forth tags |
5081 * Ignore anything after \ followed by space or in ( ) | |
5082 * Look for words defined by : | |
5083 * Look for constant, code, create, defer, value, and variable | |
5084 * OBP extensions: Look for buffer:, field, | |
5085 * Ideas by Eduardo Horvath <eeh@netbsd.org> (2004) | |
5086 */ | |
5087 static void | |
5088 Forth_words (inf) | |
5089 FILE *inf; | |
5090 { | |
5091 register char *bp; | |
5092 | |
5093 LOOP_ON_INPUT_LINES (inf, lb, bp) | |
5094 while ((bp = skip_spaces (bp))[0] != '\0') | |
5095 if (bp[0] == '\\' && iswhite(bp[1])) | |
5096 break; /* read next line */ | |
5097 else if (bp[0] == '(' && iswhite(bp[1])) | |
5098 do /* skip to ) or eol */ | |
5099 bp++; | |
5100 while (*bp != ')' && *bp != '\0'); | |
5101 else if ((bp[0] == ':' && iswhite(bp[1]) && bp++) | |
5102 || LOOKING_AT_NOCASE (bp, "constant") | |
5103 || LOOKING_AT_NOCASE (bp, "code") | |
5104 || LOOKING_AT_NOCASE (bp, "create") | |
5105 || LOOKING_AT_NOCASE (bp, "defer") | |
5106 || LOOKING_AT_NOCASE (bp, "value") | |
5107 || LOOKING_AT_NOCASE (bp, "variable") | |
5108 || LOOKING_AT_NOCASE (bp, "buffer:") | |
5109 || LOOKING_AT_NOCASE (bp, "field")) | |
5110 get_tag (skip_spaces (bp), NULL); /* Yay! A definition! */ | |
5111 else | |
5112 bp = skip_non_spaces (bp); | |
5113 } | |
5114 | |
5115 | |
5116 /* | |
428 | 5117 * Scheme tag functions |
5118 * look for (def... xyzzy | |
2225 | 5119 * (def... (xyzzy |
5120 * (def ... ((...(xyzzy .... | |
5121 * (set! xyzzy | |
5122 * Original code by Ken Haase (1985?) | |
428 | 5123 */ |
442 | 5124 static void |
428 | 5125 Scheme_functions (inf) |
5126 FILE *inf; | |
5127 { | |
5128 register char *bp; | |
5129 | |
5130 LOOP_ON_INPUT_LINES (inf, lb, bp) | |
5131 { | |
2225 | 5132 if (strneq (bp, "(def", 4) || strneq (bp, "(DEF", 4)) |
428 | 5133 { |
2225 | 5134 bp = skip_non_spaces (bp+4); |
428 | 5135 /* Skip over open parens and white space */ |
2225 | 5136 while (notinname (*bp)) |
428 | 5137 bp++; |
2225 | 5138 get_tag (bp, NULL); |
428 | 5139 } |
709 | 5140 if (LOOKING_AT (bp, "(SET!") || LOOKING_AT (bp, "(set!")) |
2225 | 5141 get_tag (bp, NULL); |
428 | 5142 } |
5143 } | |
458 | 5144 |
428 | 5145 |
5146 /* Find tags in TeX and LaTeX input files. */ | |
5147 | |
5148 /* TEX_toktab is a table of TeX control sequences that define tags. | |
2225 | 5149 * Each entry records one such control sequence. |
5150 * | |
5151 * Original code from who knows whom. | |
5152 * Ideas by: | |
5153 * Stefan Monnier (2002) | |
5154 */ | |
5155 | |
5156 static linebuffer *TEX_toktab = NULL; /* Table with tag tokens */ | |
428 | 5157 |
5158 /* Default set of control sequences to put into TEX_toktab. | |
5159 The value of environment var TEXTAGS is prepended to this. */ | |
2225 | 5160 static char *TEX_defenv = "\ |
428 | 5161 :chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\ |
2225 | 5162 :part:appendix:entry:index:def\ |
5163 :newcommand:renewcommand:newenvironment:renewenvironment"; | |
428 | 5164 |
709 | 5165 static void TEX_mode __P((FILE *)); |
2225 | 5166 static void TEX_decode_env __P((char *, char *)); |
5167 | |
5168 static char TEX_esc = '\\'; | |
5169 static char TEX_opgrp = '{'; | |
5170 static char TEX_clgrp = '}'; | |
428 | 5171 |
5172 /* | |
5173 * TeX/LaTeX scanning loop. | |
5174 */ | |
442 | 5175 static void |
458 | 5176 TeX_commands (inf) |
428 | 5177 FILE *inf; |
5178 { | |
2225 | 5179 char *cp; |
5180 linebuffer *key; | |
428 | 5181 |
5182 /* Select either \ or ! as escape character. */ | |
5183 TEX_mode (inf); | |
5184 | |
5185 /* Initialize token table once from environment. */ | |
2225 | 5186 if (TEX_toktab == NULL) |
5187 TEX_decode_env ("TEXTAGS", TEX_defenv); | |
428 | 5188 |
5189 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
5190 { | |
2225 | 5191 /* Look at each TEX keyword in line. */ |
5192 for (;;) | |
428 | 5193 { |
2225 | 5194 /* Look for a TEX escape. */ |
5195 while (*cp++ != TEX_esc) | |
5196 if (cp[-1] == '\0' || cp[-1] == '%') | |
5197 goto tex_next_line; | |
5198 | |
5199 for (key = TEX_toktab; key->buffer != NULL; key++) | |
5200 if (strneq (cp, key->buffer, key->len)) | |
5201 { | |
5202 register char *p; | |
5203 int namelen, linelen; | |
5204 bool opgrp = FALSE; | |
5205 | |
5206 cp = skip_spaces (cp + key->len); | |
5207 if (*cp == TEX_opgrp) | |
5208 { | |
5209 opgrp = TRUE; | |
5210 cp++; | |
5211 } | |
5212 for (p = cp; | |
5213 (!iswhite (*p) && *p != '#' && | |
5214 *p != TEX_opgrp && *p != TEX_clgrp); | |
5215 p++) | |
5216 continue; | |
5217 namelen = p - cp; | |
5218 linelen = lb.len; | |
5219 if (!opgrp || *p == TEX_clgrp) | |
5220 { | |
5221 while (*p != '\0' && *p != TEX_opgrp && *p != TEX_clgrp) | |
3876 | 5222 p++; |
2225 | 5223 linelen = p - lb.buffer + 1; |
5224 } | |
5225 make_tag (cp, namelen, TRUE, | |
5226 lb.buffer, linelen, lineno, linecharno); | |
5227 goto tex_next_line; /* We only tag a line once */ | |
5228 } | |
428 | 5229 } |
2225 | 5230 tex_next_line: |
5231 ; | |
428 | 5232 } |
5233 } | |
5234 | |
5235 #define TEX_LESC '\\' | |
5236 #define TEX_SESC '!' | |
5237 | |
5238 /* Figure out whether TeX's escapechar is '\\' or '!' and set grouping | |
5239 chars accordingly. */ | |
442 | 5240 static void |
428 | 5241 TEX_mode (inf) |
5242 FILE *inf; | |
5243 { | |
5244 int c; | |
5245 | |
5246 while ((c = getc (inf)) != EOF) | |
5247 { | |
5248 /* Skip to next line if we hit the TeX comment char. */ | |
2225 | 5249 if (c == '%') |
3876 | 5250 while (c != '\n' && c != EOF) |
428 | 5251 c = getc (inf); |
5252 else if (c == TEX_LESC || c == TEX_SESC ) | |
5253 break; | |
5254 } | |
5255 | |
5256 if (c == TEX_LESC) | |
5257 { | |
5258 TEX_esc = TEX_LESC; | |
5259 TEX_opgrp = '{'; | |
5260 TEX_clgrp = '}'; | |
5261 } | |
5262 else | |
5263 { | |
5264 TEX_esc = TEX_SESC; | |
5265 TEX_opgrp = '<'; | |
5266 TEX_clgrp = '>'; | |
5267 } | |
5268 /* If the input file is compressed, inf is a pipe, and rewind may fail. | |
5269 No attempt is made to correct the situation. */ | |
5270 rewind (inf); | |
5271 } | |
5272 | |
5273 /* Read environment and prepend it to the default string. | |
5274 Build token table. */ | |
2225 | 5275 static void |
428 | 5276 TEX_decode_env (evarname, defenv) |
5277 char *evarname; | |
5278 char *defenv; | |
5279 { | |
5280 register char *env, *p; | |
2225 | 5281 int i, len; |
428 | 5282 |
5283 /* Append default string to environment. */ | |
5284 env = getenv (evarname); | |
5285 if (!env) | |
5286 env = defenv; | |
5287 else | |
5288 { | |
5289 char *oldenv = env; | |
5290 env = concat (oldenv, defenv, ""); | |
5291 } | |
5292 | |
5293 /* Allocate a token table */ | |
2225 | 5294 for (len = 1, p = env; p;) |
428 | 5295 if ((p = etags_strchr (p, ':')) && *++p != '\0') |
2225 | 5296 len++; |
5297 TEX_toktab = xnew (len, linebuffer); | |
428 | 5298 |
5299 /* Unpack environment string into token table. Be careful about */ | |
5300 /* zero-length strings (leading ':', "::" and trailing ':') */ | |
2225 | 5301 for (i = 0; *env != '\0';) |
428 | 5302 { |
5303 p = etags_strchr (env, ':'); | |
5304 if (!p) /* End of environment string. */ | |
5305 p = env + strlen (env); | |
5306 if (p - env > 0) | |
5307 { /* Only non-zero strings. */ | |
2225 | 5308 TEX_toktab[i].buffer = savenstr (env, p - env); |
5309 TEX_toktab[i].len = p - env; | |
428 | 5310 i++; |
5311 } | |
5312 if (*p) | |
5313 env = p + 1; | |
5314 else | |
5315 { | |
2225 | 5316 TEX_toktab[i].buffer = NULL; /* Mark end of table. */ |
5317 TEX_toktab[i].len = 0; | |
428 | 5318 break; |
5319 } | |
5320 } | |
5321 } | |
458 | 5322 |
5323 | |
5324 /* Texinfo support. Dave Love, Mar. 2000. */ | |
5325 static void | |
5326 Texinfo_nodes (inf) | |
5327 FILE * inf; | |
5328 { | |
5329 char *cp, *start; | |
5330 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
709 | 5331 if (LOOKING_AT (cp, "@node")) |
5332 { | |
5333 start = cp; | |
5334 while (*cp != '\0' && *cp != ',') | |
5335 cp++; | |
2225 | 5336 make_tag (start, cp - start, TRUE, |
5337 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
5338 } | |
5339 } | |
5340 | |
5341 | |
5342 /* | |
5343 * HTML support. | |
5344 * Contents of <title>, <h1>, <h2>, <h3> are tags. | |
5345 * Contents of <a name=xxx> are tags with name xxx. | |
5346 * | |
5347 * Francesco Potort́, 2002. | |
5348 */ | |
5349 static void | |
5350 HTML_labels (inf) | |
5351 FILE * inf; | |
5352 { | |
5353 bool getnext = FALSE; /* next text outside of HTML tags is a tag */ | |
5354 bool skiptag = FALSE; /* skip to the end of the current HTML tag */ | |
5355 bool intag = FALSE; /* inside an html tag, looking for ID= */ | |
5356 bool inanchor = FALSE; /* when INTAG, is an anchor, look for NAME= */ | |
5357 char *end; | |
5358 | |
5359 | |
5360 linebuffer_setlen (&token_name, 0); /* no name in buffer */ | |
5361 | |
5362 LOOP_ON_INPUT_LINES (inf, lb, dbp) | |
5363 for (;;) /* loop on the same line */ | |
5364 { | |
5365 if (skiptag) /* skip HTML tag */ | |
5366 { | |
5367 while (*dbp != '\0' && *dbp != '>') | |
5368 dbp++; | |
5369 if (*dbp == '>') | |
5370 { | |
5371 dbp += 1; | |
5372 skiptag = FALSE; | |
5373 continue; /* look on the same line */ | |
5374 } | |
5375 break; /* go to next line */ | |
5376 } | |
5377 | |
5378 else if (intag) /* look for "name=" or "id=" */ | |
5379 { | |
5380 while (*dbp != '\0' && *dbp != '>' | |
5381 && lowcase (*dbp) != 'n' && lowcase (*dbp) != 'i') | |
5382 dbp++; | |
5383 if (*dbp == '\0') | |
5384 break; /* go to next line */ | |
5385 if (*dbp == '>') | |
5386 { | |
5387 dbp += 1; | |
5388 intag = FALSE; | |
5389 continue; /* look on the same line */ | |
5390 } | |
5391 if ((inanchor && LOOKING_AT_NOCASE (dbp, "name=")) | |
5392 || LOOKING_AT_NOCASE (dbp, "id=")) | |
5393 { | |
5394 bool quoted = (dbp[0] == '"'); | |
5395 | |
5396 if (quoted) | |
5397 for (end = ++dbp; *end != '\0' && *end != '"'; end++) | |
5398 continue; | |
5399 else | |
5400 for (end = dbp; *end != '\0' && intoken (*end); end++) | |
5401 continue; | |
5402 linebuffer_setlen (&token_name, end - dbp); | |
5403 strncpy (token_name.buffer, dbp, end - dbp); | |
5404 token_name.buffer[end - dbp] = '\0'; | |
5405 | |
5406 dbp = end; | |
5407 intag = FALSE; /* we found what we looked for */ | |
5408 skiptag = TRUE; /* skip to the end of the tag */ | |
5409 getnext = TRUE; /* then grab the text */ | |
5410 continue; /* look on the same line */ | |
5411 } | |
5412 dbp += 1; | |
5413 } | |
5414 | |
5415 else if (getnext) /* grab next tokens and tag them */ | |
5416 { | |
5417 dbp = skip_spaces (dbp); | |
5418 if (*dbp == '\0') | |
5419 break; /* go to next line */ | |
5420 if (*dbp == '<') | |
5421 { | |
5422 intag = TRUE; | |
5423 inanchor = (lowcase (dbp[1]) == 'a' && !intoken (dbp[2])); | |
5424 continue; /* look on the same line */ | |
5425 } | |
5426 | |
5427 for (end = dbp + 1; *end != '\0' && *end != '<'; end++) | |
5428 continue; | |
5429 make_tag (token_name.buffer, token_name.len, TRUE, | |
5430 dbp, end - dbp, lineno, linecharno); | |
5431 linebuffer_setlen (&token_name, 0); /* no name in buffer */ | |
5432 getnext = FALSE; | |
5433 break; /* go to next line */ | |
5434 } | |
5435 | |
5436 else /* look for an interesting HTML tag */ | |
5437 { | |
5438 while (*dbp != '\0' && *dbp != '<') | |
5439 dbp++; | |
5440 if (*dbp == '\0') | |
5441 break; /* go to next line */ | |
5442 intag = TRUE; | |
5443 if (lowcase (dbp[1]) == 'a' && !intoken (dbp[2])) | |
5444 { | |
5445 inanchor = TRUE; | |
5446 continue; /* look on the same line */ | |
5447 } | |
5448 else if (LOOKING_AT_NOCASE (dbp, "<title>") | |
5449 || LOOKING_AT_NOCASE (dbp, "<h1>") | |
5450 || LOOKING_AT_NOCASE (dbp, "<h2>") | |
5451 || LOOKING_AT_NOCASE (dbp, "<h3>")) | |
5452 { | |
5453 intag = FALSE; | |
5454 getnext = TRUE; | |
5455 continue; /* look on the same line */ | |
5456 } | |
5457 dbp += 1; | |
5458 } | |
709 | 5459 } |
458 | 5460 } |
5461 | |
428 | 5462 |
5463 /* | |
2225 | 5464 * Prolog support |
428 | 5465 * |
709 | 5466 * Assumes that the predicate or rule starts at column 0. |
5467 * Only the first clause of a predicate or rule is added. | |
2225 | 5468 * Original code by Sunichirou Sugou (1989) |
5469 * Rewritten by Anders Lindgren (1996) | |
428 | 5470 */ |
709 | 5471 static int prolog_pr __P((char *, char *)); |
5472 static void prolog_skip_comment __P((linebuffer *, FILE *)); | |
5473 static int prolog_atom __P((char *, int)); | |
442 | 5474 |
5475 static void | |
428 | 5476 Prolog_functions (inf) |
5477 FILE *inf; | |
5478 { | |
5479 char *cp, *last; | |
5480 int len; | |
5481 int allocated; | |
5482 | |
5483 allocated = 0; | |
5484 len = 0; | |
5485 last = NULL; | |
5486 | |
5487 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
5488 { | |
5489 if (cp[0] == '\0') /* Empty line */ | |
5490 continue; | |
442 | 5491 else if (iswhite (cp[0])) /* Not a predicate */ |
428 | 5492 continue; |
5493 else if (cp[0] == '/' && cp[1] == '*') /* comment. */ | |
5494 prolog_skip_comment (&lb, inf); | |
709 | 5495 else if ((len = prolog_pr (cp, last)) > 0) |
428 | 5496 { |
709 | 5497 /* Predicate or rule. Store the function name so that we |
5498 only generate a tag for the first clause. */ | |
428 | 5499 if (last == NULL) |
5500 last = xnew(len + 1, char); | |
5501 else if (len + 1 > allocated) | |
458 | 5502 xrnew (last, len + 1, char); |
428 | 5503 allocated = len + 1; |
5504 strncpy (last, cp, len); | |
5505 last[len] = '\0'; | |
5506 } | |
5507 } | |
3517 | 5508 if (last != NULL) |
5509 free (last); | |
428 | 5510 } |
5511 | |
5512 | |
442 | 5513 static void |
428 | 5514 prolog_skip_comment (plb, inf) |
5515 linebuffer *plb; | |
5516 FILE *inf; | |
5517 { | |
5518 char *cp; | |
5519 | |
5520 do | |
5521 { | |
5522 for (cp = plb->buffer; *cp != '\0'; cp++) | |
5523 if (cp[0] == '*' && cp[1] == '/') | |
5524 return; | |
2225 | 5525 readline (plb, inf); |
428 | 5526 } |
5527 while (!feof(inf)); | |
5528 } | |
5529 | |
5530 /* | |
709 | 5531 * A predicate or rule definition is added if it matches: |
428 | 5532 * <beginning of line><Prolog Atom><whitespace>( |
709 | 5533 * or <beginning of line><Prolog Atom><whitespace>:- |
428 | 5534 * |
5535 * It is added to the tags database if it doesn't match the | |
5536 * name of the previous clause header. | |
5537 * | |
709 | 5538 * Return the size of the name of the predicate or rule, or 0 if no |
5539 * header was found. | |
428 | 5540 */ |
442 | 5541 static int |
709 | 5542 prolog_pr (s, last) |
428 | 5543 char *s; |
5544 char *last; /* Name of last clause. */ | |
5545 { | |
5546 int pos; | |
5547 int len; | |
5548 | |
5549 pos = prolog_atom (s, 0); | |
5550 if (pos < 1) | |
5551 return 0; | |
5552 | |
5553 len = pos; | |
5554 pos = skip_spaces (s + pos) - s; | |
5555 | |
709 | 5556 if ((s[pos] == '.' |
5557 || (s[pos] == '(' && (pos += 1)) | |
5558 || (s[pos] == ':' && s[pos + 1] == '-' && (pos += 2))) | |
5559 && (last == NULL /* save only the first clause */ | |
2325 | 5560 || len != (int)strlen (last) |
709 | 5561 || !strneq (s, last, len))) |
428 | 5562 { |
2225 | 5563 make_tag (s, len, TRUE, s, pos, lineno, linecharno); |
428 | 5564 return len; |
5565 } | |
709 | 5566 else |
5567 return 0; | |
428 | 5568 } |
5569 | |
5570 /* | |
5571 * Consume a Prolog atom. | |
5572 * Return the number of bytes consumed, or -1 if there was an error. | |
5573 * | |
5574 * A prolog atom, in this context, could be one of: | |
5575 * - An alphanumeric sequence, starting with a lower case letter. | |
5576 * - A quoted arbitrary string. Single quotes can escape themselves. | |
5577 * Backslash quotes everything. | |
5578 */ | |
442 | 5579 static int |
428 | 5580 prolog_atom (s, pos) |
5581 char *s; | |
5582 int pos; | |
5583 { | |
5584 int origpos; | |
5585 | |
5586 origpos = pos; | |
5587 | |
458 | 5588 if (ISLOWER(s[pos]) || (s[pos] == '_')) |
428 | 5589 { |
5590 /* The atom is unquoted. */ | |
5591 pos++; | |
458 | 5592 while (ISALNUM(s[pos]) || (s[pos] == '_')) |
428 | 5593 { |
5594 pos++; | |
5595 } | |
5596 return pos - origpos; | |
5597 } | |
5598 else if (s[pos] == '\'') | |
5599 { | |
5600 pos++; | |
5601 | |
2225 | 5602 for (;;) |
428 | 5603 { |
5604 if (s[pos] == '\'') | |
5605 { | |
5606 pos++; | |
5607 if (s[pos] != '\'') | |
5608 break; | |
5609 pos++; /* A double quote */ | |
5610 } | |
5611 else if (s[pos] == '\0') | |
5612 /* Multiline quoted atoms are ignored. */ | |
5613 return -1; | |
5614 else if (s[pos] == '\\') | |
5615 { | |
5616 if (s[pos+1] == '\0') | |
5617 return -1; | |
5618 pos += 2; | |
5619 } | |
5620 else | |
5621 pos++; | |
5622 } | |
5623 return pos - origpos; | |
5624 } | |
5625 else | |
5626 return -1; | |
5627 } | |
458 | 5628 |
428 | 5629 |
5630 /* | |
2225 | 5631 * Support for Erlang |
428 | 5632 * |
5633 * Generates tags for functions, defines, and records. | |
5634 * Assumes that Erlang functions start at column 0. | |
2225 | 5635 * Original code by Anders Lindgren (1996) |
428 | 5636 */ |
709 | 5637 static int erlang_func __P((char *, char *)); |
5638 static void erlang_attribute __P((char *)); | |
2225 | 5639 static int erlang_atom __P((char *)); |
442 | 5640 |
5641 static void | |
428 | 5642 Erlang_functions (inf) |
5643 FILE *inf; | |
5644 { | |
5645 char *cp, *last; | |
5646 int len; | |
5647 int allocated; | |
5648 | |
5649 allocated = 0; | |
5650 len = 0; | |
5651 last = NULL; | |
5652 | |
5653 LOOP_ON_INPUT_LINES (inf, lb, cp) | |
5654 { | |
5655 if (cp[0] == '\0') /* Empty line */ | |
5656 continue; | |
442 | 5657 else if (iswhite (cp[0])) /* Not function nor attribute */ |
428 | 5658 continue; |
5659 else if (cp[0] == '%') /* comment */ | |
5660 continue; | |
5661 else if (cp[0] == '"') /* Sometimes, strings start in column one */ | |
5662 continue; | |
5663 else if (cp[0] == '-') /* attribute, e.g. "-define" */ | |
5664 { | |
5665 erlang_attribute (cp); | |
3517 | 5666 if (last != NULL) |
5667 { | |
5668 free (last); | |
5669 last = NULL; | |
5670 } | |
428 | 5671 } |
5672 else if ((len = erlang_func (cp, last)) > 0) | |
5673 { | |
5674 /* | |
5675 * Function. Store the function name so that we only | |
5676 * generates a tag for the first clause. | |
5677 */ | |
5678 if (last == NULL) | |
5679 last = xnew (len + 1, char); | |
5680 else if (len + 1 > allocated) | |
458 | 5681 xrnew (last, len + 1, char); |
428 | 5682 allocated = len + 1; |
5683 strncpy (last, cp, len); | |
5684 last[len] = '\0'; | |
5685 } | |
5686 } | |
3517 | 5687 if (last != NULL) |
5688 free (last); | |
428 | 5689 } |
5690 | |
5691 | |
5692 /* | |
5693 * A function definition is added if it matches: | |
5694 * <beginning of line><Erlang Atom><whitespace>( | |
5695 * | |
5696 * It is added to the tags database if it doesn't match the | |
5697 * name of the previous clause header. | |
5698 * | |
5699 * Return the size of the name of the function, or 0 if no function | |
5700 * was found. | |
5701 */ | |
442 | 5702 static int |
428 | 5703 erlang_func (s, last) |
5704 char *s; | |
5705 char *last; /* Name of last clause. */ | |
5706 { | |
5707 int pos; | |
5708 int len; | |
5709 | |
2225 | 5710 pos = erlang_atom (s); |
428 | 5711 if (pos < 1) |
5712 return 0; | |
5713 | |
5714 len = pos; | |
5715 pos = skip_spaces (s + pos) - s; | |
5716 | |
5717 /* Save only the first clause. */ | |
5718 if (s[pos++] == '(' | |
5719 && (last == NULL | |
5720 || len != (int)strlen (last) | |
5721 || !strneq (s, last, len))) | |
5722 { | |
2225 | 5723 make_tag (s, len, TRUE, s, pos, lineno, linecharno); |
428 | 5724 return len; |
5725 } | |
5726 | |
5727 return 0; | |
5728 } | |
5729 | |
5730 | |
5731 /* | |
5732 * Handle attributes. Currently, tags are generated for defines | |
5733 * and records. | |
5734 * | |
5735 * They are on the form: | |
5736 * -define(foo, bar). | |
5737 * -define(Foo(M, N), M+N). | |
5738 * -record(graph, {vtab = notable, cyclic = true}). | |
5739 */ | |
442 | 5740 static void |
428 | 5741 erlang_attribute (s) |
5742 char *s; | |
5743 { | |
2225 | 5744 char *cp = s; |
5745 | |
5746 if ((LOOKING_AT (cp, "-define") || LOOKING_AT (cp, "-record")) | |
5747 && *cp++ == '(') | |
428 | 5748 { |
2225 | 5749 int len = erlang_atom (skip_spaces (cp)); |
5750 if (len > 0) | |
5751 make_tag (cp, len, TRUE, s, cp + len - s, lineno, linecharno); | |
428 | 5752 } |
5753 return; | |
5754 } | |
5755 | |
5756 | |
5757 /* | |
5758 * Consume an Erlang atom (or variable). | |
5759 * Return the number of bytes consumed, or -1 if there was an error. | |
5760 */ | |
442 | 5761 static int |
2225 | 5762 erlang_atom (s) |
428 | 5763 char *s; |
5764 { | |
2225 | 5765 int pos = 0; |
428 | 5766 |
458 | 5767 if (ISALPHA (s[pos]) || s[pos] == '_') |
428 | 5768 { |
5769 /* The atom is unquoted. */ | |
2225 | 5770 do |
428 | 5771 pos++; |
2225 | 5772 while (ISALNUM (s[pos]) || s[pos] == '_'); |
428 | 5773 } |
5774 else if (s[pos] == '\'') | |
5775 { | |
2225 | 5776 for (pos++; s[pos] != '\''; pos++) |
5777 if (s[pos] == '\0' /* multiline quoted atoms are ignored */ | |
5778 || (s[pos] == '\\' && s[++pos] == '\0')) | |
5779 return 0; | |
428 | 5780 pos++; |
5781 } | |
2225 | 5782 |
5783 return pos; | |
428 | 5784 } |
458 | 5785 |
428 | 5786 |
709 | 5787 static char *scan_separators __P((char *)); |
2225 | 5788 static void add_regex __P((char *, language *)); |
709 | 5789 static char *substitute __P((char *, char *, struct re_registers *)); |
442 | 5790 |
2225 | 5791 /* |
5792 * Take a string like "/blah/" and turn it into "blah", verifying | |
5793 * that the first and last characters are the same, and handling | |
5794 * quoted separator characters. Actually, stops on the occurrence of | |
5795 * an unquoted separator. Also process \t, \n, etc. and turn into | |
5796 * appropriate characters. Works in place. Null terminates name string. | |
5797 * Returns pointer to terminating separator, or NULL for | |
5798 * unterminated regexps. | |
5799 */ | |
428 | 5800 static char * |
5801 scan_separators (name) | |
5802 char *name; | |
5803 { | |
5804 char sep = name[0]; | |
5805 char *copyto = name; | |
5806 bool quoted = FALSE; | |
5807 | |
5808 for (++name; *name != '\0'; ++name) | |
5809 { | |
5810 if (quoted) | |
5811 { | |
2225 | 5812 switch (*name) |
428 | 5813 { |
2225 | 5814 case 'a': *copyto++ = '\007'; break; /* BEL (bell) */ |
5815 case 'b': *copyto++ = '\b'; break; /* BS (back space) */ | |
5816 case 'd': *copyto++ = 0177; break; /* DEL (delete) */ | |
5817 case 'e': *copyto++ = 033; break; /* ESC (delete) */ | |
5818 case 'f': *copyto++ = '\f'; break; /* FF (form feed) */ | |
5819 case 'n': *copyto++ = '\n'; break; /* NL (new line) */ | |
5820 case 'r': *copyto++ = '\r'; break; /* CR (carriage return) */ | |
5821 case 't': *copyto++ = '\t'; break; /* TAB (horizontal tab) */ | |
5822 case 'v': *copyto++ = '\v'; break; /* VT (vertical tab) */ | |
5823 default: | |
5824 if (*name == sep) | |
5825 *copyto++ = sep; | |
5826 else | |
5827 { | |
5828 /* Something else is quoted, so preserve the quote. */ | |
5829 *copyto++ = '\\'; | |
5830 *copyto++ = *name; | |
5831 } | |
5832 break; | |
428 | 5833 } |
5834 quoted = FALSE; | |
5835 } | |
5836 else if (*name == '\\') | |
5837 quoted = TRUE; | |
5838 else if (*name == sep) | |
5839 break; | |
5840 else | |
5841 *copyto++ = *name; | |
5842 } | |
2225 | 5843 if (*name != sep) |
5844 name = NULL; /* signal unterminated regexp */ | |
428 | 5845 |
5846 /* Terminate copied string. */ | |
5847 *copyto = '\0'; | |
5848 return name; | |
5849 } | |
5850 | |
5851 /* Look at the argument of --regex or --no-regex and do the right | |
5852 thing. Same for each line of a regexp file. */ | |
442 | 5853 static void |
2225 | 5854 analyse_regex (regex_arg) |
428 | 5855 char *regex_arg; |
5856 { | |
5857 if (regex_arg == NULL) | |
709 | 5858 { |
2225 | 5859 free_regexps (); /* --no-regex: remove existing regexps */ |
709 | 5860 return; |
5861 } | |
428 | 5862 |
5863 /* A real --regexp option or a line in a regexp file. */ | |
5864 switch (regex_arg[0]) | |
5865 { | |
5866 /* Comments in regexp file or null arg to --regex. */ | |
5867 case '\0': | |
5868 case ' ': | |
5869 case '\t': | |
5870 break; | |
5871 | |
5872 /* Read a regex file. This is recursive and may result in a | |
5873 loop, which will stop when the file descriptors are exhausted. */ | |
5874 case '@': | |
5875 { | |
5876 FILE *regexfp; | |
5877 linebuffer regexbuf; | |
5878 char *regexfile = regex_arg + 1; | |
5879 | |
5880 /* regexfile is a file containing regexps, one per line. */ | |
5881 regexfp = fopen (regexfile, "r"); | |
5882 if (regexfp == NULL) | |
5883 { | |
5884 pfatal (regexfile); | |
5885 return; | |
5886 } | |
2225 | 5887 linebuffer_init (®exbuf); |
428 | 5888 while (readline_internal (®exbuf, regexfp) > 0) |
2225 | 5889 analyse_regex (regexbuf.buffer); |
428 | 5890 free (regexbuf.buffer); |
5891 fclose (regexfp); | |
5892 } | |
5893 break; | |
5894 | |
5895 /* Regexp to be used for a specific language only. */ | |
5896 case '{': | |
5897 { | |
5898 language *lang; | |
5899 char *lang_name = regex_arg + 1; | |
5900 char *cp; | |
5901 | |
5902 for (cp = lang_name; *cp != '}'; cp++) | |
5903 if (*cp == '\0') | |
5904 { | |
5905 error ("unterminated language name in regex: %s", regex_arg); | |
5906 return; | |
5907 } | |
2225 | 5908 *cp++ = '\0'; |
458 | 5909 lang = get_language_from_langname (lang_name); |
428 | 5910 if (lang == NULL) |
5911 return; | |
2225 | 5912 add_regex (cp, lang); |
428 | 5913 } |
5914 break; | |
5915 | |
5916 /* Regexp to be used for any language. */ | |
5917 default: | |
2225 | 5918 add_regex (regex_arg, NULL); |
428 | 5919 break; |
5920 } | |
5921 } | |
5922 | |
2225 | 5923 /* Separate the regexp pattern, compile it, |
5924 and care for optional name and modifiers. */ | |
442 | 5925 static void |
2225 | 5926 add_regex (regexp_pattern, lang) |
428 | 5927 char *regexp_pattern; |
5928 language *lang; | |
5929 { | |
531 | 5930 static struct re_pattern_buffer zeropattern; |
2225 | 5931 char sep, *pat, *name, *modifiers; |
428 | 5932 const char *err; |
5933 struct re_pattern_buffer *patbuf; | |
2225 | 5934 regexp *rp; |
5935 bool | |
5936 force_explicit_name = TRUE, /* do not use implicit tag names */ | |
5937 ignore_case = FALSE, /* case is significant */ | |
5938 multi_line = FALSE, /* matches are done one line at a time */ | |
5939 single_line = FALSE; /* dot does not match newline */ | |
5940 | |
5941 | |
5942 if (strlen(regexp_pattern) < 3) | |
5943 { | |
5944 error ("null regexp", (char *)NULL); | |
5945 return; | |
5946 } | |
5947 sep = regexp_pattern[0]; | |
5948 name = scan_separators (regexp_pattern); | |
5949 if (name == NULL) | |
428 | 5950 { |
5951 error ("%s: unterminated regexp", regexp_pattern); | |
5952 return; | |
5953 } | |
2225 | 5954 if (name[1] == sep) |
428 | 5955 { |
2225 | 5956 error ("null name for regexp \"%s\"", regexp_pattern); |
428 | 5957 return; |
5958 } | |
2225 | 5959 modifiers = scan_separators (name); |
5960 if (modifiers == NULL) /* no terminating separator --> no name */ | |
5961 { | |
5962 modifiers = name; | |
5963 name = ""; | |
5964 } | |
5965 else | |
5966 modifiers += 1; /* skip separator */ | |
5967 | |
5968 /* Parse regex modifiers. */ | |
5969 for (; modifiers[0] != '\0'; modifiers++) | |
5970 switch (modifiers[0]) | |
5971 { | |
5972 case 'N': | |
5973 if (modifiers == name) | |
5974 error ("forcing explicit tag name but no name, ignoring", NULL); | |
5975 force_explicit_name = TRUE; | |
5976 break; | |
5977 case 'i': | |
5978 ignore_case = TRUE; | |
5979 break; | |
5980 case 's': | |
5981 single_line = TRUE; | |
5982 /* FALLTHRU */ | |
5983 case 'm': | |
5984 multi_line = TRUE; | |
5985 need_filebuf = TRUE; | |
5986 break; | |
5987 default: | |
5988 { | |
5989 char wrongmod [2]; | |
5990 wrongmod[0] = modifiers[0]; | |
5991 wrongmod[1] = '\0'; | |
5992 error ("invalid regexp modifier `%s', ignoring", wrongmod); | |
5993 } | |
5994 break; | |
5995 } | |
428 | 5996 |
5997 patbuf = xnew (1, struct re_pattern_buffer); | |
531 | 5998 *patbuf = zeropattern; |
5999 if (ignore_case) | |
2225 | 6000 { |
6001 static char lc_trans[CHARS]; | |
6002 int i; | |
6003 for (i = 0; i < CHARS; i++) | |
6004 lc_trans[i] = lowcase (i); | |
6005 patbuf->translate = lc_trans; /* translation table to fold case */ | |
6006 } | |
6007 | |
6008 if (multi_line) | |
6009 pat = concat ("^", regexp_pattern, ""); /* anchor to beginning of line */ | |
6010 else | |
6011 pat = regexp_pattern; | |
6012 | |
6013 if (single_line) | |
6014 re_set_syntax (RE_SYNTAX_EMACS | RE_DOT_NEWLINE); | |
6015 else | |
6016 re_set_syntax (RE_SYNTAX_EMACS); | |
6017 | |
6018 err = re_compile_pattern (pat, strlen (regexp_pattern), patbuf); | |
6019 if (multi_line) | |
6020 free (pat); | |
428 | 6021 if (err != NULL) |
6022 { | |
6023 error ("%s while compiling pattern", err); | |
6024 return; | |
6025 } | |
6026 | |
2225 | 6027 rp = p_head; |
6028 p_head = xnew (1, regexp); | |
6029 p_head->pattern = savestr (regexp_pattern); | |
6030 p_head->p_next = rp; | |
709 | 6031 p_head->lang = lang; |
6032 p_head->pat = patbuf; | |
2225 | 6033 p_head->name = savestr (name); |
428 | 6034 p_head->error_signaled = FALSE; |
2225 | 6035 p_head->force_explicit_name = force_explicit_name; |
6036 p_head->ignore_case = ignore_case; | |
6037 p_head->multi_line = multi_line; | |
428 | 6038 } |
6039 | |
6040 /* | |
6041 * Do the substitutions indicated by the regular expression and | |
6042 * arguments. | |
6043 */ | |
6044 static char * | |
6045 substitute (in, out, regs) | |
6046 char *in, *out; | |
6047 struct re_registers *regs; | |
6048 { | |
6049 char *result, *t; | |
6050 int size, dig, diglen; | |
6051 | |
6052 result = NULL; | |
6053 size = strlen (out); | |
6054 | |
6055 /* Pass 1: figure out how much to allocate by finding all \N strings. */ | |
6056 if (out[size - 1] == '\\') | |
6057 fatal ("pattern error in \"%s\"", out); | |
6058 for (t = etags_strchr (out, '\\'); | |
6059 t != NULL; | |
6060 t = etags_strchr (t + 2, '\\')) | |
458 | 6061 if (ISDIGIT (t[1])) |
428 | 6062 { |
6063 dig = t[1] - '0'; | |
6064 diglen = regs->end[dig] - regs->start[dig]; | |
6065 size += diglen - 2; | |
6066 } | |
6067 else | |
6068 size -= 1; | |
6069 | |
6070 /* Allocate space and do the substitutions. */ | |
2225 | 6071 assert (size >= 0); |
428 | 6072 result = xnew (size + 1, char); |
6073 | |
6074 for (t = result; *out != '\0'; out++) | |
458 | 6075 if (*out == '\\' && ISDIGIT (*++out)) |
428 | 6076 { |
6077 dig = *out - '0'; | |
6078 diglen = regs->end[dig] - regs->start[dig]; | |
6079 strncpy (t, in + regs->start[dig], diglen); | |
6080 t += diglen; | |
6081 } | |
6082 else | |
6083 *t++ = *out; | |
6084 *t = '\0'; | |
6085 | |
2225 | 6086 assert (t <= result + size); |
6087 assert (t - result == (int)strlen (result)); | |
428 | 6088 |
6089 return result; | |
6090 } | |
6091 | |
2225 | 6092 /* Deallocate all regexps. */ |
442 | 6093 static void |
2225 | 6094 free_regexps () |
428 | 6095 { |
2225 | 6096 regexp *rp; |
428 | 6097 while (p_head != NULL) |
6098 { | |
2225 | 6099 rp = p_head->p_next; |
6100 free (p_head->pattern); | |
6101 free (p_head->name); | |
428 | 6102 free (p_head); |
2225 | 6103 p_head = rp; |
428 | 6104 } |
6105 return; | |
6106 } | |
2225 | 6107 |
6108 /* | |
6109 * Reads the whole file as a single string from `filebuf' and looks for | |
6110 * multi-line regular expressions, creating tags on matches. | |
6111 * readline already dealt with normal regexps. | |
6112 * | |
6113 * Idea by Ben Wing <ben@666.com> (2002). | |
6114 */ | |
6115 static void | |
6116 regex_tag_multiline () | |
6117 { | |
6118 char *buffer = filebuf.buffer; | |
6119 regexp *rp; | |
6120 char *name; | |
6121 | |
6122 for (rp = p_head; rp != NULL; rp = rp->p_next) | |
6123 { | |
6124 int match = 0; | |
6125 | |
6126 if (!rp->multi_line) | |
6127 continue; /* skip normal regexps */ | |
6128 | |
6129 /* Generic initialisations before parsing file from memory. */ | |
6130 lineno = 1; /* reset global line number */ | |
6131 charno = 0; /* reset global char number */ | |
6132 linecharno = 0; /* reset global char number of line start */ | |
6133 | |
6134 /* Only use generic regexps or those for the current language. */ | |
6135 if (rp->lang != NULL && rp->lang != curfdp->lang) | |
6136 continue; | |
6137 | |
6138 while (match >= 0 && match < filebuf.len) | |
6139 { | |
6140 match = re_search (rp->pat, buffer, filebuf.len, charno, | |
6141 filebuf.len - match, &rp->regs); | |
6142 switch (match) | |
6143 { | |
6144 case -2: | |
6145 /* Some error. */ | |
6146 if (!rp->error_signaled) | |
6147 { | |
6148 error ("regexp stack overflow while matching \"%s\"", | |
6149 rp->pattern); | |
6150 rp->error_signaled = TRUE; | |
6151 } | |
6152 break; | |
6153 case -1: | |
6154 /* No match. */ | |
6155 break; | |
6156 default: | |
6157 if (match == rp->regs.end[0]) | |
6158 { | |
6159 if (!rp->error_signaled) | |
6160 { | |
6161 error ("regexp matches the empty string: \"%s\"", | |
6162 rp->pattern); | |
6163 rp->error_signaled = TRUE; | |
6164 } | |
6165 match = -3; /* exit from while loop */ | |
6166 break; | |
6167 } | |
6168 | |
6169 /* Match occurred. Construct a tag. */ | |
6170 while (charno < rp->regs.end[0]) | |
6171 if (buffer[charno++] == '\n') | |
6172 lineno++, linecharno = charno; | |
6173 name = rp->name; | |
6174 if (name[0] == '\0') | |
6175 name = NULL; | |
6176 else /* make a named tag */ | |
6177 name = substitute (buffer, rp->name, &rp->regs); | |
6178 if (rp->force_explicit_name) | |
6179 /* Force explicit tag name, if a name is there. */ | |
6180 pfnote (name, TRUE, buffer + linecharno, | |
6181 charno - linecharno + 1, lineno, linecharno); | |
6182 else | |
6183 make_tag (name, strlen (name), TRUE, buffer + linecharno, | |
6184 charno - linecharno + 1, lineno, linecharno); | |
6185 break; | |
6186 } | |
6187 } | |
6188 } | |
6189 } | |
6190 | |
428 | 6191 |
2225 | 6192 static bool |
6193 nocase_tail (cp) | |
6194 char *cp; | |
428 | 6195 { |
2225 | 6196 register int len = 0; |
6197 | |
6198 while (*cp != '\0' && lowcase (*cp) == lowcase (dbp[len])) | |
6199 cp++, len++; | |
6200 if (*cp == '\0' && !intoken (dbp[len])) | |
6201 { | |
6202 dbp += len; | |
6203 return TRUE; | |
6204 } | |
6205 return FALSE; | |
428 | 6206 } |
6207 | |
442 | 6208 static void |
2225 | 6209 get_tag (bp, namepp) |
6210 register char *bp; | |
6211 char **namepp; | |
428 | 6212 { |
2225 | 6213 register char *cp = bp; |
6214 | |
6215 if (*bp != '\0') | |
6216 { | |
6217 /* Go till you get to white space or a syntactic break */ | |
6218 for (cp = bp + 1; !notinname (*cp); cp++) | |
6219 continue; | |
6220 make_tag (bp, cp - bp, TRUE, | |
6221 lb.buffer, cp - lb.buffer + 1, lineno, linecharno); | |
6222 } | |
6223 | |
6224 if (namepp != NULL) | |
6225 *namepp = savenstr (bp, cp - bp); | |
428 | 6226 } |
6227 | |
6228 /* | |
6229 * Read a line of text from `stream' into `lbp', excluding the | |
6230 * newline or CR-NL, if any. Return the number of characters read from | |
6231 * `stream', which is the length of the line including the newline. | |
6232 * | |
2225 | 6233 * On DOS or Windows we do not count the CR character, if any before the |
6234 * NL, in the returned length; this mirrors the behavior of Emacs on those | |
428 | 6235 * platforms (for text files, it translates CR-NL to NL as it reads in the |
6236 * file). | |
2225 | 6237 * |
6238 * If multi-line regular expressions are requested, each line read is | |
6239 * appended to `filebuf'. | |
428 | 6240 */ |
442 | 6241 static long |
428 | 6242 readline_internal (lbp, stream) |
6243 linebuffer *lbp; | |
6244 register FILE *stream; | |
6245 { | |
6246 char *buffer = lbp->buffer; | |
6247 register char *p = lbp->buffer; | |
6248 register char *pend; | |
6249 int chars_deleted; | |
6250 | |
6251 pend = p + lbp->size; /* Separate to avoid 386/IX compiler bug. */ | |
6252 | |
2225 | 6253 for (;;) |
428 | 6254 { |
6255 register int c = getc (stream); | |
6256 if (p == pend) | |
6257 { | |
6258 /* We're at the end of linebuffer: expand it. */ | |
6259 lbp->size *= 2; | |
458 | 6260 xrnew (buffer, lbp->size, char); |
428 | 6261 p += buffer - lbp->buffer; |
6262 pend = buffer + lbp->size; | |
6263 lbp->buffer = buffer; | |
6264 } | |
6265 if (c == EOF) | |
6266 { | |
6267 *p = '\0'; | |
6268 chars_deleted = 0; | |
6269 break; | |
6270 } | |
6271 if (c == '\n') | |
6272 { | |
6273 if (p > buffer && p[-1] == '\r') | |
6274 { | |
6275 p -= 1; | |
458 | 6276 #ifdef DOS_NT |
428 | 6277 /* Assume CRLF->LF translation will be performed by Emacs |
6278 when loading this file, so CRs won't appear in the buffer. | |
6279 It would be cleaner to compensate within Emacs; | |
6280 however, Emacs does not know how many CRs were deleted | |
6281 before any given point in the file. */ | |
6282 chars_deleted = 1; | |
6283 #else | |
6284 chars_deleted = 2; | |
6285 #endif | |
6286 } | |
6287 else | |
6288 { | |
6289 chars_deleted = 1; | |
6290 } | |
6291 *p = '\0'; | |
6292 break; | |
6293 } | |
6294 *p++ = c; | |
6295 } | |
6296 lbp->len = p - buffer; | |
6297 | |
2225 | 6298 if (need_filebuf /* we need filebuf for multi-line regexps */ |
6299 && chars_deleted > 0) /* not at EOF */ | |
6300 { | |
6301 while (filebuf.size <= filebuf.len + lbp->len + 1) /* +1 for \n */ | |
6302 { | |
6303 /* Expand filebuf. */ | |
6304 filebuf.size *= 2; | |
6305 xrnew (filebuf.buffer, filebuf.size, char); | |
6306 } | |
6307 strncpy (filebuf.buffer + filebuf.len, lbp->buffer, lbp->len); | |
6308 filebuf.len += lbp->len; | |
6309 filebuf.buffer[filebuf.len++] = '\n'; | |
6310 filebuf.buffer[filebuf.len] = '\0'; | |
6311 } | |
6312 | |
428 | 6313 return lbp->len + chars_deleted; |
6314 } | |
6315 | |
6316 /* | |
6317 * Like readline_internal, above, but in addition try to match the | |
2225 | 6318 * input line against relevant regular expressions and manage #line |
6319 * directives. | |
428 | 6320 */ |
2225 | 6321 static void |
428 | 6322 readline (lbp, stream) |
6323 linebuffer *lbp; | |
6324 FILE *stream; | |
6325 { | |
2225 | 6326 long result; |
6327 | |
6328 linecharno = charno; /* update global char number of line start */ | |
6329 result = readline_internal (lbp, stream); /* read line */ | |
6330 lineno += 1; /* increment global line number */ | |
6331 charno += result; /* increment global char number */ | |
6332 | |
6333 /* Honour #line directives. */ | |
6334 if (!no_line_directive) | |
6335 { | |
6336 static bool discard_until_line_directive; | |
6337 | |
6338 /* Check whether this is a #line directive. */ | |
6339 if (result > 12 && strneq (lbp->buffer, "#line ", 6)) | |
6340 { | |
3876 | 6341 unsigned int lno; |
6342 int start = 0; | |
6343 | |
6344 if (sscanf (lbp->buffer, "#line %u \"%n", &lno, &start) >= 1 | |
6345 && start > 0) /* double quote character found */ | |
2225 | 6346 { |
6347 char *endp = lbp->buffer + start; | |
6348 | |
6349 while ((endp = etags_strchr (endp, '"')) != NULL | |
6350 && endp[-1] == '\\') | |
6351 endp++; | |
6352 if (endp != NULL) | |
6353 /* Ok, this is a real #line directive. Let's deal with it. */ | |
6354 { | |
6355 char *taggedabsname; /* absolute name of original file */ | |
6356 char *taggedfname; /* name of original file as given */ | |
6357 char *name; /* temp var */ | |
6358 | |
6359 discard_until_line_directive = FALSE; /* found it */ | |
6360 name = lbp->buffer + start; | |
6361 *endp = '\0'; | |
6362 canonicalize_filename (name); /* for DOS */ | |
3876 | 6363 taggedabsname = absolute_filename (name, tagfiledir); |
2225 | 6364 if (filename_is_absolute (name) |
6365 || filename_is_absolute (curfdp->infname)) | |
6366 taggedfname = savestr (taggedabsname); | |
6367 else | |
6368 taggedfname = relative_filename (taggedabsname,tagfiledir); | |
6369 | |
6370 if (streq (curfdp->taggedfname, taggedfname)) | |
6371 /* The #line directive is only a line number change. We | |
6372 deal with this afterwards. */ | |
6373 free (taggedfname); | |
6374 else | |
6375 /* The tags following this #line directive should be | |
6376 attributed to taggedfname. In order to do this, set | |
6377 curfdp accordingly. */ | |
6378 { | |
6379 fdesc *fdp; /* file description pointer */ | |
6380 | |
6381 /* Go look for a file description already set up for the | |
6382 file indicated in the #line directive. If there is | |
6383 one, use it from now until the next #line | |
6384 directive. */ | |
6385 for (fdp = fdhead; fdp != NULL; fdp = fdp->next) | |
6386 if (streq (fdp->infname, curfdp->infname) | |
6387 && streq (fdp->taggedfname, taggedfname)) | |
6388 /* If we remove the second test above (after the &&) | |
6389 then all entries pertaining to the same file are | |
6390 coalesced in the tags file. If we use it, then | |
6391 entries pertaining to the same file but generated | |
6392 from different files (via #line directives) will | |
6393 go into separate sections in the tags file. These | |
6394 alternatives look equivalent. The first one | |
6395 destroys some apparently useless information. */ | |
6396 { | |
6397 curfdp = fdp; | |
6398 free (taggedfname); | |
6399 break; | |
6400 } | |
6401 /* Else, if we already tagged the real file, skip all | |
6402 input lines until the next #line directive. */ | |
6403 if (fdp == NULL) /* not found */ | |
6404 for (fdp = fdhead; fdp != NULL; fdp = fdp->next) | |
6405 if (streq (fdp->infabsname, taggedabsname)) | |
6406 { | |
6407 discard_until_line_directive = TRUE; | |
6408 free (taggedfname); | |
6409 break; | |
6410 } | |
6411 /* Else create a new file description and use that from | |
6412 now on, until the next #line directive. */ | |
6413 if (fdp == NULL) /* not found */ | |
6414 { | |
6415 fdp = fdhead; | |
6416 fdhead = xnew (1, fdesc); | |
6417 *fdhead = *curfdp; /* copy curr. file description */ | |
6418 fdhead->next = fdp; | |
6419 fdhead->infname = savestr (curfdp->infname); | |
6420 fdhead->infabsname = savestr (curfdp->infabsname); | |
6421 fdhead->infabsdir = savestr (curfdp->infabsdir); | |
6422 fdhead->taggedfname = taggedfname; | |
6423 fdhead->usecharno = FALSE; | |
6424 fdhead->prop = NULL; | |
6425 fdhead->written = FALSE; | |
6426 curfdp = fdhead; | |
6427 } | |
6428 } | |
6429 free (taggedabsname); | |
6430 lineno = lno - 1; | |
6431 readline (lbp, stream); | |
6432 return; | |
6433 } /* if a real #line directive */ | |
6434 } /* if #line is followed by a a number */ | |
6435 } /* if line begins with "#line " */ | |
6436 | |
6437 /* If we are here, no #line directive was found. */ | |
6438 if (discard_until_line_directive) | |
6439 { | |
6440 if (result > 0) | |
6441 { | |
6442 /* Do a tail recursion on ourselves, thus discarding the contents | |
6443 of the line buffer. */ | |
6444 readline (lbp, stream); | |
6445 return; | |
6446 } | |
6447 /* End of file. */ | |
6448 discard_until_line_directive = FALSE; | |
6449 return; | |
6450 } | |
6451 } /* if #line directives should be considered */ | |
6452 | |
6453 { | |
6454 int match; | |
6455 regexp *rp; | |
6456 char *name; | |
6457 | |
6458 /* Match against relevant regexps. */ | |
6459 if (lbp->len > 0) | |
6460 for (rp = p_head; rp != NULL; rp = rp->p_next) | |
6461 { | |
6462 /* Only use generic regexps or those for the current language. | |
6463 Also do not use multiline regexps, which is the job of | |
6464 regex_tag_multiline. */ | |
6465 if ((rp->lang != NULL && rp->lang != fdhead->lang) | |
6466 || rp->multi_line) | |
6467 continue; | |
6468 | |
6469 match = re_match (rp->pat, lbp->buffer, lbp->len, 0, &rp->regs); | |
6470 switch (match) | |
6471 { | |
6472 case -2: | |
6473 /* Some error. */ | |
6474 if (!rp->error_signaled) | |
6475 { | |
6476 error ("regexp stack overflow while matching \"%s\"", | |
6477 rp->pattern); | |
6478 rp->error_signaled = TRUE; | |
6479 } | |
6480 break; | |
6481 case -1: | |
6482 /* No match. */ | |
6483 break; | |
6484 case 0: | |
6485 /* Empty string matched. */ | |
6486 if (!rp->error_signaled) | |
6487 { | |
6488 error ("regexp matches the empty string: \"%s\"", rp->pattern); | |
6489 rp->error_signaled = TRUE; | |
6490 } | |
6491 break; | |
6492 default: | |
6493 /* Match occurred. Construct a tag. */ | |
6494 name = rp->name; | |
6495 if (name[0] == '\0') | |
6496 name = NULL; | |
6497 else /* make a named tag */ | |
6498 name = substitute (lbp->buffer, rp->name, &rp->regs); | |
6499 if (rp->force_explicit_name) | |
6500 /* Force explicit tag name, if a name is there. */ | |
6501 pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno); | |
6502 else | |
6503 make_tag (name, strlen (name), TRUE, | |
6504 lbp->buffer, match, lineno, linecharno); | |
6505 break; | |
6506 } | |
6507 } | |
6508 } | |
428 | 6509 } |
458 | 6510 |
428 | 6511 |
6512 /* | |
6513 * Return a pointer to a space of size strlen(cp)+1 allocated | |
6514 * with xnew where the string CP has been copied. | |
6515 */ | |
442 | 6516 static char * |
428 | 6517 savestr (cp) |
6518 char *cp; | |
6519 { | |
6520 return savenstr (cp, strlen (cp)); | |
6521 } | |
6522 | |
6523 /* | |
6524 * Return a pointer to a space of size LEN+1 allocated with xnew where | |
6525 * the string CP has been copied for at most the first LEN characters. | |
6526 */ | |
442 | 6527 static char * |
428 | 6528 savenstr (cp, len) |
6529 char *cp; | |
6530 int len; | |
6531 { | |
6532 register char *dp; | |
6533 | |
6534 dp = xnew (len + 1, char); | |
6535 strncpy (dp, cp, len); | |
6536 dp[len] = '\0'; | |
6537 return dp; | |
6538 } | |
6539 | |
6540 /* | |
6541 * Return the ptr in sp at which the character c last | |
6542 * appears; NULL if not found | |
442 | 6543 * |
6544 * Identical to POSIX strrchr, included for portability. | |
428 | 6545 */ |
442 | 6546 static char * |
428 | 6547 etags_strrchr (sp, c) |
442 | 6548 register const char *sp; |
6549 register int c; | |
428 | 6550 { |
438 | 6551 register const char *r; |
428 | 6552 |
6553 r = NULL; | |
6554 do | |
6555 { | |
6556 if (*sp == c) | |
6557 r = sp; | |
6558 } while (*sp++); | |
442 | 6559 return (char *)r; |
428 | 6560 } |
6561 | |
6562 /* | |
6563 * Return the ptr in sp at which the character c first | |
6564 * appears; NULL if not found | |
442 | 6565 * |
6566 * Identical to POSIX strchr, included for portability. | |
428 | 6567 */ |
442 | 6568 static char * |
428 | 6569 etags_strchr (sp, c) |
442 | 6570 register const char *sp; |
6571 register int c; | |
428 | 6572 { |
6573 do | |
6574 { | |
6575 if (*sp == c) | |
442 | 6576 return (char *)sp; |
428 | 6577 } while (*sp++); |
6578 return NULL; | |
6579 } | |
6580 | |
2225 | 6581 /* |
6582 * Compare two strings, ignoring case for alphabetic characters. | |
6583 * | |
6584 * Same as BSD's strcasecmp, included for portability. | |
6585 */ | |
6586 static int | |
6587 etags_strcasecmp (s1, s2) | |
6588 register const char *s1; | |
6589 register const char *s2; | |
6590 { | |
6591 while (*s1 != '\0' | |
6592 && (ISALPHA (*s1) && ISALPHA (*s2) | |
6593 ? lowcase (*s1) == lowcase (*s2) | |
6594 : *s1 == *s2)) | |
6595 s1++, s2++; | |
6596 | |
6597 return (ISALPHA (*s1) && ISALPHA (*s2) | |
6598 ? lowcase (*s1) - lowcase (*s2) | |
6599 : *s1 - *s2); | |
6600 } | |
6601 | |
6602 /* | |
6603 * Compare two strings, ignoring case for alphabetic characters. | |
6604 * Stop after a given number of characters | |
6605 * | |
6606 * Same as BSD's strncasecmp, included for portability. | |
6607 */ | |
6608 static int | |
6609 etags_strncasecmp (s1, s2, n) | |
6610 register const char *s1; | |
6611 register const char *s2; | |
6612 register int n; | |
6613 { | |
6614 while (*s1 != '\0' && n-- > 0 | |
6615 && (ISALPHA (*s1) && ISALPHA (*s2) | |
6616 ? lowcase (*s1) == lowcase (*s2) | |
6617 : *s1 == *s2)) | |
6618 s1++, s2++; | |
6619 | |
6620 if (n < 0) | |
6621 return 0; | |
6622 else | |
6623 return (ISALPHA (*s1) && ISALPHA (*s2) | |
6624 ? lowcase (*s1) - lowcase (*s2) | |
6625 : *s1 - *s2); | |
6626 } | |
6627 | |
2554 | 6628 /* Skip spaces (end of string is not space), return new pointer. */ |
442 | 6629 static char * |
428 | 6630 skip_spaces (cp) |
6631 char *cp; | |
6632 { | |
442 | 6633 while (iswhite (*cp)) |
428 | 6634 cp++; |
6635 return cp; | |
6636 } | |
6637 | |
2554 | 6638 /* Skip non spaces, except end of string, return new pointer. */ |
442 | 6639 static char * |
428 | 6640 skip_non_spaces (cp) |
6641 char *cp; | |
6642 { | |
442 | 6643 while (*cp != '\0' && !iswhite (*cp)) |
428 | 6644 cp++; |
6645 return cp; | |
6646 } | |
6647 | |
6648 /* Print error message and exit. */ | |
458 | 6649 void |
428 | 6650 fatal (s1, s2) |
6651 char *s1, *s2; | |
6652 { | |
6653 error (s1, s2); | |
2225 | 6654 exit (EXIT_FAILURE); |
428 | 6655 } |
6656 | |
442 | 6657 static void |
428 | 6658 pfatal (s1) |
6659 char *s1; | |
6660 { | |
6661 perror (s1); | |
2225 | 6662 exit (EXIT_FAILURE); |
428 | 6663 } |
6664 | |
442 | 6665 static void |
428 | 6666 suggest_asking_for_help () |
6667 { | |
2325 | 6668 fprintf (stderr, "\tTry `%s %s' for a complete list of options.\n", |
3517 | 6669 progname, NO_LONG_OPTIONS ? "-h" : "--help"); |
2225 | 6670 exit (EXIT_FAILURE); |
428 | 6671 } |
6672 | |
6673 /* Print error message. `s1' is printf control string, `s2' is arg for it. */ | |
442 | 6674 static void |
428 | 6675 error (s1, s2) |
6676 const char *s1, *s2; | |
6677 { | |
6678 fprintf (stderr, "%s: ", progname); | |
6679 fprintf (stderr, s1, s2); | |
6680 fprintf (stderr, "\n"); | |
6681 } | |
6682 | |
6683 /* Return a newly-allocated string whose contents | |
6684 concatenate those of s1, s2, s3. */ | |
442 | 6685 static char * |
428 | 6686 concat (s1, s2, s3) |
6687 char *s1, *s2, *s3; | |
6688 { | |
6689 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); | |
6690 char *result = xnew (len1 + len2 + len3 + 1, char); | |
6691 | |
6692 strcpy (result, s1); | |
6693 strcpy (result + len1, s2); | |
6694 strcpy (result + len1 + len2, s3); | |
6695 result[len1 + len2 + len3] = '\0'; | |
6696 | |
6697 return result; | |
6698 } | |
458 | 6699 |
428 | 6700 |
6701 /* Does the same work as the system V getcwd, but does not need to | |
6702 guess the buffer size in advance. */ | |
442 | 6703 static char * |
428 | 6704 etags_getcwd () |
6705 { | |
6706 #ifdef HAVE_GETCWD | |
6707 int bufsize = 200; | |
6708 char *path = xnew (bufsize, char); | |
6709 | |
6710 while (getcwd (path, bufsize) == NULL) | |
6711 { | |
6712 if (errno != ERANGE) | |
6713 pfatal ("getcwd"); | |
6714 bufsize *= 2; | |
6715 free (path); | |
6716 path = xnew (bufsize, char); | |
6717 } | |
6718 | |
6719 canonicalize_filename (path); | |
6720 return path; | |
6721 | |
6722 #else /* not HAVE_GETCWD */ | |
458 | 6723 #if MSDOS |
6724 | |
6725 char *p, path[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */ | |
6726 | |
6727 getwd (path); | |
6728 | |
6729 for (p = path; *p != '\0'; p++) | |
6730 if (*p == '\\') | |
6731 *p = '/'; | |
6732 else | |
6733 *p = lowcase (*p); | |
6734 | |
6735 return strdup (path); | |
6736 #else /* not MSDOS */ | |
428 | 6737 linebuffer path; |
6738 FILE *pipe; | |
6739 | |
2225 | 6740 linebuffer_init (&path); |
428 | 6741 pipe = (FILE *) popen ("pwd 2>/dev/null", "r"); |
6742 if (pipe == NULL || readline_internal (&path, pipe) == 0) | |
6743 pfatal ("pwd"); | |
6744 pclose (pipe); | |
6745 | |
6746 return path.buffer; | |
458 | 6747 #endif /* not MSDOS */ |
428 | 6748 #endif /* not HAVE_GETCWD */ |
6749 } | |
6750 | |
6751 /* Return a newly allocated string containing the file name of FILE | |
6752 relative to the absolute directory DIR (which should end with a slash). */ | |
442 | 6753 static char * |
428 | 6754 relative_filename (file, dir) |
6755 char *file, *dir; | |
6756 { | |
6757 char *fp, *dp, *afn, *res; | |
6758 int i; | |
6759 | |
6760 /* Find the common root of file and dir (with a trailing slash). */ | |
6761 afn = absolute_filename (file, cwd); | |
6762 fp = afn; | |
6763 dp = dir; | |
6764 while (*fp++ == *dp++) | |
6765 continue; | |
6766 fp--, dp--; /* back to the first differing char */ | |
458 | 6767 #ifdef DOS_NT |
428 | 6768 if (fp == afn && afn[0] != '/') /* cannot build a relative name */ |
6769 return afn; | |
6770 #endif | |
6771 do /* look at the equal chars until '/' */ | |
6772 fp--, dp--; | |
6773 while (*fp != '/'); | |
6774 | |
6775 /* Build a sequence of "../" strings for the resulting relative file name. */ | |
6776 i = 0; | |
6777 while ((dp = etags_strchr (dp + 1, '/')) != NULL) | |
6778 i += 1; | |
6779 res = xnew (3*i + strlen (fp + 1) + 1, char); | |
6780 res[0] = '\0'; | |
6781 while (i-- > 0) | |
6782 strcat (res, "../"); | |
6783 | |
6784 /* Add the file name relative to the common root of file and dir. */ | |
6785 strcat (res, fp + 1); | |
6786 free (afn); | |
6787 | |
6788 return res; | |
6789 } | |
6790 | |
6791 /* Return a newly allocated string containing the absolute file name | |
6792 of FILE given DIR (which should end with a slash). */ | |
442 | 6793 static char * |
428 | 6794 absolute_filename (file, dir) |
6795 char *file, *dir; | |
6796 { | |
6797 char *slashp, *cp, *res; | |
6798 | |
6799 if (filename_is_absolute (file)) | |
6800 res = savestr (file); | |
458 | 6801 #ifdef DOS_NT |
428 | 6802 /* We don't support non-absolute file names with a drive |
6803 letter, like `d:NAME' (it's too much hassle). */ | |
6804 else if (file[1] == ':') | |
6805 fatal ("%s: relative file names with drive letters not supported", file); | |
6806 #endif | |
6807 else | |
6808 res = concat (dir, file, ""); | |
6809 | |
6810 /* Delete the "/dirname/.." and "/." substrings. */ | |
6811 slashp = etags_strchr (res, '/'); | |
6812 while (slashp != NULL && slashp[0] != '\0') | |
6813 { | |
6814 if (slashp[1] == '.') | |
6815 { | |
6816 if (slashp[2] == '.' | |
6817 && (slashp[3] == '/' || slashp[3] == '\0')) | |
6818 { | |
6819 cp = slashp; | |
6820 do | |
6821 cp--; | |
6822 while (cp >= res && !filename_is_absolute (cp)); | |
6823 if (cp < res) | |
6824 cp = slashp; /* the absolute name begins with "/.." */ | |
458 | 6825 #ifdef DOS_NT |
6826 /* Under MSDOS and NT we get `d:/NAME' as absolute | |
428 | 6827 file name, so the luser could say `d:/../NAME'. |
6828 We silently treat this as `d:/NAME'. */ | |
6829 else if (cp[0] != '/') | |
6830 cp = slashp; | |
6831 #endif | |
4768
0f5bee973a7b
Etags: use memmove instead of strcpy to move characters within a string. See
Jerry James <james@xemacs.org>
parents:
3993
diff
changeset
|
6832 memmove (cp, slashp + 3, strlen (slashp + 3) + 1); |
428 | 6833 slashp = cp; |
6834 continue; | |
6835 } | |
6836 else if (slashp[2] == '/' || slashp[2] == '\0') | |
6837 { | |
4768
0f5bee973a7b
Etags: use memmove instead of strcpy to move characters within a string. See
Jerry James <james@xemacs.org>
parents:
3993
diff
changeset
|
6838 memmove (slashp, slashp + 2, strlen (slashp + 2) + 1); |
428 | 6839 continue; |
6840 } | |
6841 } | |
6842 | |
6843 slashp = etags_strchr (slashp + 1, '/'); | |
6844 } | |
6845 | |
3517 | 6846 if (res[0] == '\0') /* just a safety net: should never happen */ |
6847 { | |
6848 free (res); | |
6849 return savestr ("/"); | |
6850 } | |
428 | 6851 else |
6852 return res; | |
6853 } | |
6854 | |
6855 /* Return a newly allocated string containing the absolute | |
6856 file name of dir where FILE resides given DIR (which should | |
6857 end with a slash). */ | |
442 | 6858 static char * |
428 | 6859 absolute_dirname (file, dir) |
6860 char *file, *dir; | |
6861 { | |
6862 char *slashp, *res; | |
6863 char save; | |
6864 | |
6865 canonicalize_filename (file); | |
6866 slashp = etags_strrchr (file, '/'); | |
6867 if (slashp == NULL) | |
6868 return savestr (dir); | |
6869 save = slashp[1]; | |
6870 slashp[1] = '\0'; | |
6871 res = absolute_filename (file, dir); | |
6872 slashp[1] = save; | |
6873 | |
6874 return res; | |
6875 } | |
6876 | |
6877 /* Whether the argument string is an absolute file name. The argument | |
6878 string must have been canonicalized with canonicalize_filename. */ | |
442 | 6879 static bool |
428 | 6880 filename_is_absolute (fn) |
6881 char *fn; | |
6882 { | |
6883 return (fn[0] == '/' | |
458 | 6884 #ifdef DOS_NT |
6885 || (ISALPHA(fn[0]) && fn[1] == ':' && fn[2] == '/') | |
428 | 6886 #endif |
6887 ); | |
6888 } | |
6889 | |
6890 /* Translate backslashes into slashes. Works in place. */ | |
442 | 6891 static void |
428 | 6892 canonicalize_filename (fn) |
6893 register char *fn; | |
6894 { | |
458 | 6895 #ifdef DOS_NT |
428 | 6896 /* Canonicalize drive letter case. */ |
458 | 6897 if (fn[0] != '\0' && fn[1] == ':' && ISLOWER (fn[0])) |
6898 fn[0] = upcase (fn[0]); | |
428 | 6899 /* Convert backslashes to slashes. */ |
6900 for (; *fn != '\0'; fn++) | |
6901 if (*fn == '\\') | |
6902 *fn = '/'; | |
6903 #else | |
6904 /* No action. */ | |
6905 fn = NULL; /* shut up the compiler */ | |
6906 #endif | |
6907 } | |
6908 | |
2225 | 6909 |
6910 /* Initialize a linebuffer for use */ | |
6911 static void | |
6912 linebuffer_init (lbp) | |
6913 linebuffer *lbp; | |
6914 { | |
6915 lbp->size = (DEBUG) ? 3 : 200; | |
6916 lbp->buffer = xnew (lbp->size, char); | |
6917 lbp->buffer[0] = '\0'; | |
6918 lbp->len = 0; | |
6919 } | |
6920 | |
458 | 6921 /* Set the minimum size of a string contained in a linebuffer. */ |
442 | 6922 static void |
458 | 6923 linebuffer_setlen (lbp, toksize) |
428 | 6924 linebuffer *lbp; |
6925 int toksize; | |
6926 { | |
458 | 6927 while (lbp->size <= toksize) |
6928 { | |
6929 lbp->size *= 2; | |
6930 xrnew (lbp->buffer, lbp->size, char); | |
6931 } | |
6932 lbp->len = toksize; | |
428 | 6933 } |
6934 | |
2225 | 6935 /* Like malloc but get fatal error if memory is exhausted. */ |
6936 static PTR | |
428 | 6937 xmalloc (size) |
6938 unsigned int size; | |
6939 { | |
709 | 6940 PTR result = (PTR) malloc (size); |
428 | 6941 if (result == NULL) |
6942 fatal ("virtual memory exhausted", (char *)NULL); | |
6943 return result; | |
6944 } | |
6945 | |
2225 | 6946 static PTR |
428 | 6947 xrealloc (ptr, size) |
6948 char *ptr; | |
6949 unsigned int size; | |
6950 { | |
709 | 6951 PTR result = (PTR) realloc (ptr, size); |
428 | 6952 if (result == NULL) |
6953 fatal ("virtual memory exhausted", (char *)NULL); | |
6954 return result; | |
6955 } | |
709 | 6956 |
6957 /* | |
6958 * Local Variables: | |
6959 * indent-tabs-mode: t | |
6960 * tab-width: 8 | |
2225 | 6961 * fill-column: 79 |
6962 * c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer" "fdesc" "node" "regexp") | |
3972 | 6963 * c-file-style: "gnu" |
709 | 6964 * End: |
6965 */ | |
2225 | 6966 |
6967 /* arch-tag: 8a9b748d-390c-4922-99db-2eeefa921051 | |
6968 (do not change this comment) */ | |
6969 | |
6970 /* etags.c ends here */ |