2 |
Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2001, 2002 |
Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2001, 2002 |
3 |
Free Software Foundation, Inc. and Ken Arnold |
Free Software Foundation, Inc. and Ken Arnold |
4 |
|
|
5 |
This file is not considered part of GNU Emacs. |
This file is not considered part of GNU Emacs. |
6 |
|
|
7 |
This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
8 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
9 |
the Free Software Foundation; either version 2 of the License, or |
the Free Software Foundation; either version 2 of the License, or |
10 |
(at your option) any later version. |
(at your option) any later version. |
11 |
|
|
12 |
This program is distributed in the hope that it will be useful, |
This program is distributed in the hope that it will be useful, |
13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
GNU General Public License for more details. |
GNU General Public License for more details. |
16 |
|
|
17 |
You should have received a copy of the GNU General Public License |
You should have received a copy of the GNU General Public License |
18 |
along with this program; if not, write to the Free Software Foundation, |
along with this program; if not, write to the Free Software Foundation, |
19 |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
20 |
|
|
21 |
/* |
/* |
22 |
* Authors: |
* Authors: |
34 |
* Francesco Potort́ <pot@gnu.org> has maintained it since 1993. |
* Francesco Potort́ <pot@gnu.org> has maintained it since 1993. |
35 |
*/ |
*/ |
36 |
|
|
37 |
char pot_etags_version[] = "@(#) pot revision number is 16.10"; |
char pot_etags_version[] = "@(#) pot revision number is 16.19"; |
38 |
|
|
39 |
#define TRUE 1 |
#define TRUE 1 |
40 |
#define FALSE 0 |
#define FALSE 0 |
288 |
enum { |
enum { |
289 |
at_language, /* a language specification */ |
at_language, /* a language specification */ |
290 |
at_regexp, /* a regular expression */ |
at_regexp, /* a regular expression */ |
|
at_icregexp, /* same, but with case ignored */ |
|
291 |
at_filename, /* a file name */ |
at_filename, /* a file name */ |
292 |
at_stdin /* read from stdin here */ |
at_stdin /* read from stdin here */ |
293 |
} arg_type; /* argument type */ |
} arg_type; /* argument type */ |
307 |
char *name_pattern; |
char *name_pattern; |
308 |
bool error_signaled; |
bool error_signaled; |
309 |
bool ignore_case; |
bool ignore_case; |
310 |
|
bool multi_line; |
311 |
} pattern; |
} pattern; |
312 |
#endif /* ETAGS_REGEXPS */ |
#endif /* ETAGS_REGEXPS */ |
313 |
|
|
355 |
static char *get_tag __P((char *)); |
static char *get_tag __P((char *)); |
356 |
|
|
357 |
#ifdef ETAGS_REGEXPS |
#ifdef ETAGS_REGEXPS |
358 |
static void analyse_regex __P((char *, bool)); |
static void analyse_regex __P((char *)); |
|
static void add_regex __P((char *, bool, language *)); |
|
359 |
static void free_patterns __P((void)); |
static void free_patterns __P((void)); |
360 |
|
static void regex_tag_multiline __P((void)); |
361 |
#endif /* ETAGS_REGEXPS */ |
#endif /* ETAGS_REGEXPS */ |
362 |
static void error __P((const char *, const char *)); |
static void error __P((const char *, const char *)); |
363 |
static void suggest_asking_for_help __P((void)); |
static void suggest_asking_for_help __P((void)); |
417 |
static node *last_node; /* the last node created */ |
static node *last_node; /* the last node created */ |
418 |
|
|
419 |
static linebuffer lb; /* the current line */ |
static linebuffer lb; /* the current line */ |
420 |
|
static linebuffer filebuf; /* a buffer containing the whole file */ |
421 |
|
|
422 |
/* boolean "functions" (see init) */ |
/* boolean "functions" (see init) */ |
423 |
static bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS]; |
static bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS]; |
458 |
static bool parsing_stdin; /* --parse-stdin used */ |
static bool parsing_stdin; /* --parse-stdin used */ |
459 |
|
|
460 |
#ifdef ETAGS_REGEXPS |
#ifdef ETAGS_REGEXPS |
461 |
/* List of all regexps. */ |
static pattern *p_head; /* list of all regexps */ |
462 |
static pattern *p_head; |
static bool need_filebuf; /* some regexes are multi-line */ |
463 |
|
#else |
464 |
/* How many characters in the character set. (From regex.c.) */ |
# define need_filebuf FALSE |
|
#define CHAR_SET_SIZE 256 |
|
|
/* Translation table for case-insensitive matching. */ |
|
|
static char lc_trans[CHAR_SET_SIZE]; |
|
465 |
#endif /* ETAGS_REGEXPS */ |
#endif /* ETAGS_REGEXPS */ |
466 |
|
|
467 |
#ifdef LONG_OPTIONS |
#ifdef LONG_OPTIONS |
678 |
} |
} |
679 |
|
|
680 |
#ifndef EMACS_NAME |
#ifndef EMACS_NAME |
681 |
# define EMACS_NAME "GNU Emacs" |
# define EMACS_NAME "standalone" |
682 |
#endif |
#endif |
683 |
#ifndef VERSION |
#ifndef VERSION |
684 |
# define VERSION "21" |
# define VERSION "version" |
685 |
#endif |
#endif |
686 |
static void |
static void |
687 |
print_version () |
print_version () |
773 |
REGEXP is anchored (as if preceded by ^).\n\ |
REGEXP is anchored (as if preceded by ^).\n\ |
774 |
The form /REGEXP/NAME/ creates a named tag.\n\ |
The form /REGEXP/NAME/ creates a named tag.\n\ |
775 |
For example Tcl named tags can be created with:\n\ |
For example Tcl named tags can be created with:\n\ |
776 |
--regex=\"/proc[ \\t]+\\([^ \\t]+\\)/\\1/.\""); |
--regex=\"/proc[ \\t]+\\([^ \\t]+\\)/\\1/.\"."); |
777 |
puts ("-c /REGEXP/, --ignore-case-regex=/REGEXP/ or --ignore-case-regex=@regexfile\n\ |
puts ("In the form /REGEXP/MODS or /REGEXP/NAME/MODS, MODS are\n\ |
778 |
Like -r, --regex but ignore case when matching expressions."); |
one-letter modifiers: `i' means to ignore case, `m' means\n\ |
779 |
|
allow multi-line matches, `s' implies `m' and additionally\n\ |
780 |
|
causes dot to match the newline character."); |
781 |
puts ("-R, --no-regex\n\ |
puts ("-R, --no-regex\n\ |
782 |
Don't create tags from regexps for the following files."); |
Don't create tags from regexps for the following files."); |
783 |
#endif /* ETAGS_REGEXPS */ |
#endif /* ETAGS_REGEXPS */ |
996 |
is small. */ |
is small. */ |
997 |
argbuffer = xnew (argc, argument); |
argbuffer = xnew (argc, argument); |
998 |
|
|
|
#ifdef ETAGS_REGEXPS |
|
|
/* Set syntax for regular expression routines. */ |
|
|
re_set_syntax (RE_SYNTAX_EMACS | RE_INTERVALS); |
|
|
/* Translation table for case-insensitive search. */ |
|
|
for (i = 0; i < CHAR_SET_SIZE; i++) |
|
|
lc_trans[i] = lowcase (i); |
|
|
#endif /* ETAGS_REGEXPS */ |
|
|
|
|
999 |
/* |
/* |
1000 |
* If etags, always find typedefs and structure tags. Why not? |
* If etags, always find typedefs and structure tags. Why not? |
1001 |
* Also default to find macro constants, enum constants and |
* Also default to find macro constants, enum constants and |
1071 |
} |
} |
1072 |
} |
} |
1073 |
break; |
break; |
1074 |
|
case 'c': |
1075 |
|
/* Backward compatibility: support obsolete --ignore-case-regexp. */ |
1076 |
|
optarg = concat (optarg, "i", ""); /* memory leak here */ |
1077 |
|
/* FALLTHRU */ |
1078 |
case 'r': |
case 'r': |
1079 |
argbuffer[current_arg].arg_type = at_regexp; |
argbuffer[current_arg].arg_type = at_regexp; |
1080 |
argbuffer[current_arg].what = optarg; |
argbuffer[current_arg].what = optarg; |
1085 |
argbuffer[current_arg].what = NULL; |
argbuffer[current_arg].what = NULL; |
1086 |
++current_arg; |
++current_arg; |
1087 |
break; |
break; |
|
case 'c': |
|
|
argbuffer[current_arg].arg_type = at_icregexp; |
|
|
argbuffer[current_arg].what = optarg; |
|
|
++current_arg; |
|
|
break; |
|
1088 |
case 'V': |
case 'V': |
1089 |
print_version (); |
print_version (); |
1090 |
break; |
break; |
1143 |
|
|
1144 |
initbuffer (&lb); |
initbuffer (&lb); |
1145 |
initbuffer (&filename_lb); |
initbuffer (&filename_lb); |
1146 |
|
initbuffer (&filebuf); |
1147 |
|
|
1148 |
if (!CTAGS) |
if (!CTAGS) |
1149 |
{ |
{ |
1178 |
break; |
break; |
1179 |
#ifdef ETAGS_REGEXPS |
#ifdef ETAGS_REGEXPS |
1180 |
case at_regexp: |
case at_regexp: |
1181 |
analyse_regex (argbuffer[i].what, FALSE); |
analyse_regex (argbuffer[i].what); |
|
break; |
|
|
case at_icregexp: |
|
|
analyse_regex (argbuffer[i].what, TRUE); |
|
1182 |
break; |
break; |
1183 |
#endif |
#endif |
1184 |
case at_filename: |
case at_filename: |
1223 |
#ifdef ETAGS_REGEXPS |
#ifdef ETAGS_REGEXPS |
1224 |
free_patterns (); |
free_patterns (); |
1225 |
#endif /* ETAGS_REGEXPS */ |
#endif /* ETAGS_REGEXPS */ |
1226 |
|
free (filebuf.buffer); |
1227 |
|
|
1228 |
if (!CTAGS || cxref_style) |
if (!CTAGS || cxref_style) |
1229 |
{ |
{ |
1638 |
FILE *inf; |
FILE *inf; |
1639 |
{ |
{ |
1640 |
char *cp; |
char *cp; |
|
node *old_last_node; |
|
1641 |
language *lang = curfdp->lang; |
language *lang = curfdp->lang; |
1642 |
Lang_function *parser = NULL; |
Lang_function *parser = NULL; |
1643 |
|
|
1692 |
/* We rewind here, even if inf may be a pipe. We fail if the |
/* We rewind here, even if inf may be a pipe. We fail if the |
1693 |
length of the first line is longer than the pipe block size, |
length of the first line is longer than the pipe block size, |
1694 |
which is unlikely. */ |
which is unlikely. */ |
1695 |
rewind (inf); |
rewind (inf); |
1696 |
|
|
1697 |
/* Else try to guess the language given the case insensitive file name. */ |
/* Else try to guess the language given the case insensitive file name. */ |
1698 |
if (parser == NULL) |
if (parser == NULL) |
1705 |
} |
} |
1706 |
} |
} |
1707 |
|
|
1708 |
|
/* Else try Fortran or C. */ |
1709 |
|
if (parser == NULL) |
1710 |
|
{ |
1711 |
|
node *old_last_node = last_node; |
1712 |
|
|
1713 |
|
curfdp->lang = get_language_from_langname ("fortran"); |
1714 |
|
find_entries (inf); |
1715 |
|
|
1716 |
|
if (old_last_node == last_node) |
1717 |
|
/* No Fortran entries found. Try C. */ |
1718 |
|
{ |
1719 |
|
/* We do not tag if rewind fails. |
1720 |
|
Only the file name will be recorded in the tags file. */ |
1721 |
|
rewind (inf); |
1722 |
|
curfdp->lang = get_language_from_langname (cplusplus ? "c++" : "c"); |
1723 |
|
find_entries (inf); |
1724 |
|
} |
1725 |
|
return; |
1726 |
|
} |
1727 |
|
|
1728 |
if (!no_line_directive |
if (!no_line_directive |
1729 |
&& curfdp->lang != NULL && curfdp->lang->metasource) |
&& curfdp->lang != NULL && curfdp->lang->metasource) |
1730 |
/* It may be that this is a bingo.y file, and we already parsed a bingo.c |
/* It may be that this is a bingo.y file, and we already parsed a bingo.c |
1757 |
fdpp = &(*fdpp)->next; /* advance the list pointer */ |
fdpp = &(*fdpp)->next; /* advance the list pointer */ |
1758 |
} |
} |
1759 |
|
|
1760 |
if (parser != NULL) |
assert (parser != NULL); |
|
{ |
|
|
/* Generic initialisations before reading from file. */ |
|
|
lineno = 0; /* reset global line number */ |
|
|
charno = 0; /* reset global char number */ |
|
|
linecharno = 0; /* reset global char number of line start */ |
|
1761 |
|
|
1762 |
parser (inf); |
/* Generic initialisations before reading from file. */ |
1763 |
return; |
filebuf.len = 0; /* reset the file buffer */ |
|
} |
|
1764 |
|
|
1765 |
/* Else try Fortran. */ |
/* Generic initialisations before parsing file with readline. */ |
1766 |
old_last_node = last_node; |
lineno = 0; /* reset global line number */ |
1767 |
curfdp->lang = get_language_from_langname ("fortran"); |
charno = 0; /* reset global char number */ |
1768 |
find_entries (inf); |
linecharno = 0; /* reset global char number of line start */ |
1769 |
|
|
1770 |
if (old_last_node == last_node) |
parser (inf); |
1771 |
/* No Fortran entries found. Try C. */ |
|
1772 |
{ |
#ifdef ETAGS_REGEXPS |
1773 |
/* We do not tag if rewind fails. |
regex_tag_multiline (); |
1774 |
Only the file name will be recorded in the tags file. */ |
#endif /* ETAGS_REGEXPS */ |
|
rewind (inf); |
|
|
curfdp->lang = get_language_from_langname (cplusplus ? "c++" : "c"); |
|
|
find_entries (inf); |
|
|
} |
|
|
return; |
|
1775 |
} |
} |
1776 |
|
|
1777 |
|
|
2012 |
* invalidate_nodes () |
* invalidate_nodes () |
2013 |
* Scan the node tree and invalidate all nodes pointing to the |
* Scan the node tree and invalidate all nodes pointing to the |
2014 |
* given file description (CTAGS case) or free them (ETAGS case). |
* given file description (CTAGS case) or free them (ETAGS case). |
2015 |
|
* |
2016 |
|
* This function most likely contains a bug, but I cannot tell where. |
2017 |
|
* I have a case of a binary that crashes inside this function with a bus |
2018 |
|
* error. Unfortunately, the binary does not contain debug information, and |
2019 |
|
* compiling with debugging information makes the bug disappear. |
2020 |
*/ |
*/ |
2021 |
static void |
static void |
2022 |
invalidate_nodes (badfdp, npp) |
invalidate_nodes (badfdp, npp) |
2033 |
if (np->left != NULL) |
if (np->left != NULL) |
2034 |
invalidate_nodes (badfdp, &np->left); |
invalidate_nodes (badfdp, &np->left); |
2035 |
if (np->fdp == badfdp) |
if (np->fdp == badfdp) |
2036 |
np-> valid = FALSE; |
np->valid = FALSE; |
2037 |
if (np->right != NULL) |
if (np->right != NULL) |
2038 |
invalidate_nodes (badfdp, &np->right); |
invalidate_nodes (badfdp, &np->right); |
2039 |
} |
} |
5266 |
#ifdef ETAGS_REGEXPS |
#ifdef ETAGS_REGEXPS |
5267 |
|
|
5268 |
static char *scan_separators __P((char *)); |
static char *scan_separators __P((char *)); |
5269 |
static void analyse_regex __P((char *, bool)); |
static void add_regex __P((char *, language *)); |
|
static void add_regex __P((char *, bool, language *)); |
|
5270 |
static char *substitute __P((char *, char *, struct re_registers *)); |
static char *substitute __P((char *, char *, struct re_registers *)); |
5271 |
|
|
5272 |
/* Take a string like "/blah/" and turn it into "blah", making sure |
/* |
5273 |
that the first and last characters are the same, and handling |
* Take a string like "/blah/" and turn it into "blah", verifying |
5274 |
quoted separator characters. Actually, stops on the occurrence of |
* that the first and last characters are the same, and handling |
5275 |
an unquoted separator. Also turns "\t" into a Tab character, and |
* quoted separator characters. Actually, stops on the occurrence of |
5276 |
similarly for all character escape sequences supported by Gcc. |
* an unquoted separator. Also process \t, \n, etc. and turn into |
5277 |
Returns pointer to terminating separator. Works in place. Null |
* appropriate characters. Works in place. Null terminates name string. |
5278 |
terminates name string. */ |
* Returns pointer to terminating separator, or NULL for |
5279 |
|
* unterminated regexps. |
5280 |
|
*/ |
5281 |
static char * |
static char * |
5282 |
scan_separators (name) |
scan_separators (name) |
5283 |
char *name; |
char *name; |
5292 |
{ |
{ |
5293 |
switch (*name) |
switch (*name) |
5294 |
{ |
{ |
5295 |
case 'a': *copyto++ = '\007'; break; |
case 'a': *copyto++ = '\007'; break; /* BEL (bell) */ |
5296 |
case 'b': *copyto++ = '\b'; break; |
case 'b': *copyto++ = '\b'; break; /* BS (back space) */ |
5297 |
case 'd': *copyto++ = 0177; break; |
case 'd': *copyto++ = 0177; break; /* DEL (delete) */ |
5298 |
case 'e': *copyto++ = 033; break; |
case 'e': *copyto++ = 033; break; /* ESC (delete) */ |
5299 |
case 'f': *copyto++ = '\f'; break; |
case 'f': *copyto++ = '\f'; break; /* FF (form feed) */ |
5300 |
case 'n': *copyto++ = '\n'; break; |
case 'n': *copyto++ = '\n'; break; /* NL (new line) */ |
5301 |
case 'r': *copyto++ = '\r'; break; |
case 'r': *copyto++ = '\r'; break; /* CR (carriage return) */ |
5302 |
case 't': *copyto++ = '\t'; break; |
case 't': *copyto++ = '\t'; break; /* TAB (horizontal tab) */ |
5303 |
case 'v': *copyto++ = '\v'; break; |
case 'v': *copyto++ = '\v'; break; /* VT (vertical tab) */ |
5304 |
default: |
default: |
5305 |
if (*name == sep) |
if (*name == sep) |
5306 |
*copyto++ = sep; |
*copyto++ = sep; |
5321 |
else |
else |
5322 |
*copyto++ = *name; |
*copyto++ = *name; |
5323 |
} |
} |
5324 |
|
if (*name != sep) |
5325 |
|
name = NULL; /* signal unterminated regexp */ |
5326 |
|
|
5327 |
/* Terminate copied string. */ |
/* Terminate copied string. */ |
5328 |
*copyto = '\0'; |
*copyto = '\0'; |
5332 |
/* Look at the argument of --regex or --no-regex and do the right |
/* Look at the argument of --regex or --no-regex and do the right |
5333 |
thing. Same for each line of a regexp file. */ |
thing. Same for each line of a regexp file. */ |
5334 |
static void |
static void |
5335 |
analyse_regex (regex_arg, ignore_case) |
analyse_regex (regex_arg) |
5336 |
char *regex_arg; |
char *regex_arg; |
|
bool ignore_case; |
|
5337 |
{ |
{ |
5338 |
if (regex_arg == NULL) |
if (regex_arg == NULL) |
5339 |
{ |
{ |
5367 |
} |
} |
5368 |
initbuffer (®exbuf); |
initbuffer (®exbuf); |
5369 |
while (readline_internal (®exbuf, regexfp) > 0) |
while (readline_internal (®exbuf, regexfp) > 0) |
5370 |
analyse_regex (regexbuf.buffer, ignore_case); |
analyse_regex (regexbuf.buffer); |
5371 |
free (regexbuf.buffer); |
free (regexbuf.buffer); |
5372 |
fclose (regexfp); |
fclose (regexfp); |
5373 |
} |
} |
5386 |
error ("unterminated language name in regex: %s", regex_arg); |
error ("unterminated language name in regex: %s", regex_arg); |
5387 |
return; |
return; |
5388 |
} |
} |
5389 |
*cp = '\0'; |
*cp++ = '\0'; |
5390 |
lang = get_language_from_langname (lang_name); |
lang = get_language_from_langname (lang_name); |
5391 |
if (lang == NULL) |
if (lang == NULL) |
5392 |
return; |
return; |
5393 |
add_regex (cp + 1, ignore_case, lang); |
add_regex (cp, lang); |
5394 |
} |
} |
5395 |
break; |
break; |
5396 |
|
|
5397 |
/* Regexp to be used for any language. */ |
/* Regexp to be used for any language. */ |
5398 |
default: |
default: |
5399 |
add_regex (regex_arg, ignore_case, NULL); |
add_regex (regex_arg, NULL); |
5400 |
break; |
break; |
5401 |
} |
} |
5402 |
} |
} |
5404 |
/* Turn a name, which is an ed-style (but Emacs syntax) regular |
/* Turn a name, which is an ed-style (but Emacs syntax) regular |
5405 |
expression, into a real regular expression by compiling it. */ |
expression, into a real regular expression by compiling it. */ |
5406 |
static void |
static void |
5407 |
add_regex (regexp_pattern, ignore_case, lang) |
add_regex (regexp_pattern, lang) |
5408 |
char *regexp_pattern; |
char *regexp_pattern; |
|
bool ignore_case; |
|
5409 |
language *lang; |
language *lang; |
5410 |
{ |
{ |
5411 |
static struct re_pattern_buffer zeropattern; |
static struct re_pattern_buffer zeropattern; |
5412 |
char *name; |
char sep, *pat, *name, *modifiers; |
5413 |
const char *err; |
const char *err; |
5414 |
struct re_pattern_buffer *patbuf; |
struct re_pattern_buffer *patbuf; |
5415 |
pattern *pp; |
pattern *pp; |
5416 |
|
bool ignore_case, multi_line, single_line; |
5417 |
|
|
5418 |
|
|
5419 |
if (regexp_pattern[strlen(regexp_pattern)-1] != regexp_pattern[0]) |
if (strlen(regexp_pattern) < 3) |
5420 |
{ |
{ |
5421 |
error ("%s: unterminated regexp", regexp_pattern); |
error ("null regexp", (char *)NULL); |
5422 |
return; |
return; |
5423 |
} |
} |
5424 |
|
sep = regexp_pattern[0]; |
5425 |
name = scan_separators (regexp_pattern); |
name = scan_separators (regexp_pattern); |
5426 |
if (regexp_pattern[0] == '\0') |
if (name == NULL) |
5427 |
{ |
{ |
5428 |
error ("null regexp", (char *)NULL); |
error ("%s: unterminated regexp", regexp_pattern); |
5429 |
|
return; |
5430 |
|
} |
5431 |
|
if (name[1] == sep) |
5432 |
|
{ |
5433 |
|
error ("null name for regexp \"%s\"", regexp_pattern); |
5434 |
return; |
return; |
5435 |
} |
} |
5436 |
(void) scan_separators (name); |
modifiers = scan_separators (name); |
5437 |
|
if (modifiers == NULL) /* no terminating separator --> no name */ |
5438 |
|
{ |
5439 |
|
modifiers = name; |
5440 |
|
name = ""; |
5441 |
|
} |
5442 |
|
else |
5443 |
|
modifiers += 1; /* skip separator */ |
5444 |
|
|
5445 |
|
/* Parse regex modifiers. */ |
5446 |
|
ignore_case = FALSE; /* case is significant */ |
5447 |
|
multi_line = FALSE; /* matches are done one line at a time */ |
5448 |
|
single_line = FALSE; /* dot does not match newline */ |
5449 |
|
for (; modifiers[0] != '\0'; modifiers++) |
5450 |
|
switch (modifiers[0]) |
5451 |
|
{ |
5452 |
|
case 'i': |
5453 |
|
ignore_case = TRUE; |
5454 |
|
break; |
5455 |
|
case 's': |
5456 |
|
single_line = TRUE; |
5457 |
|
/* FALLTHRU */ |
5458 |
|
case 'm': |
5459 |
|
multi_line = TRUE; |
5460 |
|
need_filebuf = TRUE; |
5461 |
|
break; |
5462 |
|
default: |
5463 |
|
modifiers[1] = '\0'; |
5464 |
|
error ("invalid regexp modifier `%s'", modifiers); |
5465 |
|
return; |
5466 |
|
} |
5467 |
|
|
5468 |
patbuf = xnew (1, struct re_pattern_buffer); |
patbuf = xnew (1, struct re_pattern_buffer); |
5469 |
*patbuf = zeropattern; |
*patbuf = zeropattern; |
5470 |
if (ignore_case) |
if (ignore_case) |
5471 |
patbuf->translate = lc_trans; /* translation table to fold case */ |
{ |
5472 |
|
static char lc_trans[CHARS]; |
5473 |
|
int i; |
5474 |
|
for (i = 0; i < CHARS; i++) |
5475 |
|
lc_trans[i] = lowcase (i); |
5476 |
|
patbuf->translate = lc_trans; /* translation table to fold case */ |
5477 |
|
} |
5478 |
|
|
5479 |
|
if (multi_line) |
5480 |
|
pat = concat ("^", regexp_pattern, ""); /* anchor to beginning of line */ |
5481 |
|
else |
5482 |
|
pat = regexp_pattern; |
5483 |
|
|
5484 |
err = re_compile_pattern (regexp_pattern, strlen (regexp_pattern), patbuf); |
if (single_line) |
5485 |
|
re_set_syntax (RE_SYNTAX_EMACS | RE_DOT_NEWLINE); |
5486 |
|
else |
5487 |
|
re_set_syntax (RE_SYNTAX_EMACS); |
5488 |
|
|
5489 |
|
err = re_compile_pattern (pat, strlen (regexp_pattern), patbuf); |
5490 |
|
if (multi_line) |
5491 |
|
free (pat); |
5492 |
if (err != NULL) |
if (err != NULL) |
5493 |
{ |
{ |
5494 |
error ("%s while compiling pattern", err); |
error ("%s while compiling pattern", err); |
5504 |
p_head->name_pattern = savestr (name); |
p_head->name_pattern = savestr (name); |
5505 |
p_head->error_signaled = FALSE; |
p_head->error_signaled = FALSE; |
5506 |
p_head->ignore_case = ignore_case; |
p_head->ignore_case = ignore_case; |
5507 |
|
p_head->multi_line = multi_line; |
5508 |
} |
} |
5509 |
|
|
5510 |
/* |
/* |
5572 |
} |
} |
5573 |
return; |
return; |
5574 |
} |
} |
5575 |
|
|
5576 |
|
/* |
5577 |
|
* Reads the whole file as a single string from `filebuf' and looks for |
5578 |
|
* multi-line regular expressions, creating tags on matches. |
5579 |
|
* readline already dealt with normal regexps. |
5580 |
|
* |
5581 |
|
* Idea by Ben Wing <ben@666.com> (2002). |
5582 |
|
*/ |
5583 |
|
static void |
5584 |
|
regex_tag_multiline () |
5585 |
|
{ |
5586 |
|
char *buffer = filebuf.buffer; |
5587 |
|
pattern *pp; |
5588 |
|
|
5589 |
|
for (pp = p_head; pp != NULL; pp = pp->p_next) |
5590 |
|
{ |
5591 |
|
int match = 0; |
5592 |
|
|
5593 |
|
if (!pp->multi_line) |
5594 |
|
continue; /* skip normal regexps */ |
5595 |
|
|
5596 |
|
/* Generic initialisations before parsing file from memory. */ |
5597 |
|
lineno = 1; /* reset global line number */ |
5598 |
|
charno = 0; /* reset global char number */ |
5599 |
|
linecharno = 0; /* reset global char number of line start */ |
5600 |
|
|
5601 |
|
/* Only use generic regexps or those for the current language. */ |
5602 |
|
if (pp->lang != NULL && pp->lang != curfdp->lang) |
5603 |
|
continue; |
5604 |
|
|
5605 |
|
while (match >= 0 && match < filebuf.len) |
5606 |
|
{ |
5607 |
|
match = re_search (pp->pat, buffer, filebuf.len, charno, |
5608 |
|
filebuf.len - match, &pp->regs); |
5609 |
|
switch (match) |
5610 |
|
{ |
5611 |
|
case -2: |
5612 |
|
/* Some error. */ |
5613 |
|
if (!pp->error_signaled) |
5614 |
|
{ |
5615 |
|
error ("regexp stack overflow while matching \"%s\"", |
5616 |
|
pp->regex); |
5617 |
|
pp->error_signaled = TRUE; |
5618 |
|
} |
5619 |
|
break; |
5620 |
|
case -1: |
5621 |
|
/* No match. */ |
5622 |
|
break; |
5623 |
|
default: |
5624 |
|
if (match == pp->regs.end[0]) |
5625 |
|
{ |
5626 |
|
if (!pp->error_signaled) |
5627 |
|
{ |
5628 |
|
error ("regexp matches the empty string: \"%s\"", |
5629 |
|
pp->regex); |
5630 |
|
pp->error_signaled = TRUE; |
5631 |
|
} |
5632 |
|
match = -3; /* exit from while loop */ |
5633 |
|
break; |
5634 |
|
} |
5635 |
|
|
5636 |
|
/* Match occurred. Construct a tag. */ |
5637 |
|
while (charno < pp->regs.end[0]) |
5638 |
|
if (buffer[charno++] == '\n') |
5639 |
|
lineno++, linecharno = charno; |
5640 |
|
if (pp->name_pattern[0] != '\0') |
5641 |
|
{ |
5642 |
|
/* Make a named tag. */ |
5643 |
|
char *name = substitute (buffer, |
5644 |
|
pp->name_pattern, &pp->regs); |
5645 |
|
if (name != NULL) |
5646 |
|
pfnote (name, TRUE, buffer + linecharno, |
5647 |
|
charno - linecharno + 1, lineno, linecharno); |
5648 |
|
} |
5649 |
|
else |
5650 |
|
{ |
5651 |
|
/* Make an unnamed tag. */ |
5652 |
|
pfnote ((char *)NULL, TRUE, buffer + linecharno, |
5653 |
|
charno - linecharno + 1, lineno, linecharno); |
5654 |
|
} |
5655 |
|
break; |
5656 |
|
} |
5657 |
|
} |
5658 |
|
} |
5659 |
|
} |
5660 |
|
|
5661 |
#endif /* ETAGS_REGEXPS */ |
#endif /* ETAGS_REGEXPS */ |
5662 |
|
|
5663 |
|
|
5710 |
* newline or CR-NL, if any. Return the number of characters read from |
* newline or CR-NL, if any. Return the number of characters read from |
5711 |
* `stream', which is the length of the line including the newline. |
* `stream', which is the length of the line including the newline. |
5712 |
* |
* |
5713 |
* On DOS or Windows we do not count the CR character, if any, before the |
* On DOS or Windows we do not count the CR character, if any before the |
5714 |
* NL, in the returned length; this mirrors the behavior of emacs on those |
* NL, in the returned length; this mirrors the behavior of Emacs on those |
5715 |
* platforms (for text files, it translates CR-NL to NL as it reads in the |
* platforms (for text files, it translates CR-NL to NL as it reads in the |
5716 |
* file). |
* file). |
5717 |
|
* |
5718 |
|
* If multi-line regular expressions are requested, each line read is |
5719 |
|
* appended to `filebuf'. |
5720 |
*/ |
*/ |
5721 |
static long |
static long |
5722 |
readline_internal (lbp, stream) |
readline_internal (lbp, stream) |
5775 |
} |
} |
5776 |
lbp->len = p - buffer; |
lbp->len = p - buffer; |
5777 |
|
|
5778 |
|
if (need_filebuf /* we need filebuf for multi-line regexps */ |
5779 |
|
&& chars_deleted > 0) /* not at EOF */ |
5780 |
|
{ |
5781 |
|
while (filebuf.size <= filebuf.len + lbp->len + 1) /* +1 for \n */ |
5782 |
|
{ |
5783 |
|
/* Expand filebuf. */ |
5784 |
|
filebuf.size *= 2; |
5785 |
|
xrnew (filebuf.buffer, filebuf.size, char); |
5786 |
|
} |
5787 |
|
strncpy (filebuf.buffer + filebuf.len, lbp->buffer, lbp->len); |
5788 |
|
filebuf.len += lbp->len; |
5789 |
|
filebuf.buffer[filebuf.len++] = '\n'; |
5790 |
|
filebuf.buffer[filebuf.len] = '\0'; |
5791 |
|
} |
5792 |
|
|
5793 |
return lbp->len + chars_deleted; |
return lbp->len + chars_deleted; |
5794 |
} |
} |
5795 |
|
|
5796 |
/* |
/* |
5797 |
* Like readline_internal, above, but in addition try to match the |
* Like readline_internal, above, but in addition try to match the |
5798 |
* input line against relevant regular expressions. |
* input line against relevant regular expressions and manage #line |
5799 |
|
* directives. |
5800 |
*/ |
*/ |
5801 |
static void |
static void |
5802 |
readline (lbp, stream) |
readline (lbp, stream) |
5917 |
{ |
{ |
5918 |
if (result > 0) |
if (result > 0) |
5919 |
{ |
{ |
5920 |
/* Do a tail recursion on ourselves, thus discarding the contents |
/* Do a tail recursion on ourselves, thus discarding the contents |
5921 |
of the line buffer. */ |
of the line buffer. */ |
5922 |
readline (lbp, stream); |
readline (lbp, stream); |
5923 |
return; |
return; |
5924 |
} |
} |
5937 |
if (lbp->len > 0) |
if (lbp->len > 0) |
5938 |
for (pp = p_head; pp != NULL; pp = pp->p_next) |
for (pp = p_head; pp != NULL; pp = pp->p_next) |
5939 |
{ |
{ |
5940 |
/* Only use generic regexps or those for the current language. */ |
/* Only use generic regexps or those for the current language. |
5941 |
if (pp->lang != NULL && pp->lang != fdhead->lang) |
Also do not use multiline regexps, which is the job of |
5942 |
|
regex_tag_multiline. */ |
5943 |
|
if ((pp->lang != NULL && pp->lang != fdhead->lang) |
5944 |
|
|| pp->multi_line) |
5945 |
continue; |
continue; |
5946 |
|
|
5947 |
match = re_match (pp->pat, lbp->buffer, lbp->len, 0, &pp->regs); |
match = re_match (pp->pat, lbp->buffer, lbp->len, 0, &pp->regs); |
5951 |
/* Some error. */ |
/* Some error. */ |
5952 |
if (!pp->error_signaled) |
if (!pp->error_signaled) |
5953 |
{ |
{ |
5954 |
error ("error while matching \"%s\"", pp->regex); |
error ("regexp stack overflow while matching \"%s\"", |
5955 |
|
pp->regex); |
5956 |
pp->error_signaled = TRUE; |
pp->error_signaled = TRUE; |
5957 |
} |
} |
5958 |
break; |
break; |
5959 |
case -1: |
case -1: |
5960 |
/* No match. */ |
/* No match. */ |
5961 |
break; |
break; |
5962 |
|
case 0: |
5963 |
|
/* Empty string matched. */ |
5964 |
|
if (!pp->error_signaled) |
5965 |
|
{ |
5966 |
|
error ("regexp matches the empty string: \"%s\"", |
5967 |
|
pp->regex); |
5968 |
|
pp->error_signaled = TRUE; |
5969 |
|
} |
5970 |
|
break; |
5971 |
default: |
default: |
5972 |
/* Match occurred. Construct a tag. */ |
/* Match occurred. Construct a tag. */ |
5973 |
if (pp->name_pattern[0] != '\0') |
if (pp->name_pattern[0] != '\0') |
6407 |
* indent-tabs-mode: t |
* indent-tabs-mode: t |
6408 |
* tab-width: 8 |
* tab-width: 8 |
6409 |
* fill-column: 79 |
* fill-column: 79 |
6410 |
* c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer" "fdesc" "node") |
* c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer" "fdesc" "node" "pattern") |
6411 |
* End: |
* End: |
6412 |
*/ |
*/ |