IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2015 Free Software
+Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2016 Free Software
Foundation, Inc.
This file is not considered part of GNU Emacs.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
# define CTAGS false
#endif
-#define streq(s,t) (assert ((s)!=NULL || (t)!=NULL), !strcmp (s, t))
-#define strcaseeq(s,t) (assert ((s)!=NULL && (t)!=NULL), !c_strcasecmp (s, t))
-#define strneq(s,t,n) (assert ((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
-#define strncaseeq(s,t,n) (assert ((s)!=NULL && (t)!=NULL), !c_strncasecmp (s, t, n))
+static bool
+streq (char const *s, char const *t)
+{
+ return strcmp (s, t) == 0;
+}
+
+static bool
+strcaseeq (char const *s, char const *t)
+{
+ return c_strcasecmp (s, t) == 0;
+}
+
+static bool
+strneq (char const *s, char const *t, size_t n)
+{
+ return strncmp (s, t, n) == 0;
+}
+
+static bool
+strncaseeq (char const *s, char const *t, size_t n)
+{
+ return c_strncasecmp (s, t, n) == 0;
+}
/* C is not in a name. */
static bool
static void Erlang_functions (FILE *);
static void Forth_words (FILE *);
static void Fortran_functions (FILE *);
+static void Go_functions (FILE *);
static void HTML_labels (FILE *);
static void Lisp_functions (FILE *);
static void Lua_functions (FILE *);
static void PS_functions (FILE *);
static void Prolog_functions (FILE *);
static void Python_functions (FILE *);
+static void Ruby_functions (FILE *);
static void Scheme_functions (FILE *);
static void TeX_commands (FILE *);
static void Texinfo_nodes (FILE *);
static void free_regexps (void);
static void regex_tag_multiline (void);
static void error (const char *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
+static void verror (char const *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
static _Noreturn void suggest_asking_for_help (void);
-_Noreturn void fatal (const char *, const char *);
+static _Noreturn void fatal (char const *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
static _Noreturn void pfatal (const char *);
static void add_node (node *, node **);
static bool cplusplus; /* .[hc] means C++, not C (undocumented) */
static bool ignoreindent; /* -I: ignore indentation in C */
static int packages_only; /* --packages-only: in Ada, only tag packages*/
+static int class_qualify; /* -Q: produce class-qualified tags in C++/Java */
/* STDIN is defined in LynxOS system headers */
#ifdef STDIN
{ "members", no_argument, &members, 1 },
{ "no-members", no_argument, &members, 0 },
{ "output", required_argument, NULL, 'o' },
+ { "class-qualify", no_argument, &class_qualify, 'Q' },
{ "regex", required_argument, NULL, 'r' },
{ "no-regex", no_argument, NULL, 'R' },
{ "ignore-case-regex", required_argument, NULL, 'c' },
{ "ads", "adb", "ada", NULL };
static const char Ada_help [] =
"In Ada code, functions, procedures, packages, tasks and types are\n\
-tags. Use the `--packages-only' option to create tags for\n\
+tags. Use the '--packages-only' option to create tags for\n\
packages only.\n\
Ada tag names have suffixes indicating the type of entity:\n\
Entity type: Qualifier:\n\
package body /b\n\
type /t\n\
task /k\n\
-Thus, `M-x find-tag <RET> bidule/b <RET>' will go directly to the\n\
-body of the package `bidule', while `M-x find-tag <RET> bidule <RET>'\n\
-will just search for any tag `bidule'.";
+Thus, 'M-x find-tag <RET> bidule/b <RET>' will go directly to the\n\
+body of the package 'bidule', while 'M-x find-tag <RET> bidule <RET>'\n\
+will just search for any tag 'bidule'.";
/* Assembly code */
static const char *Asm_suffixes [] =
#if CTAGS /* C help for Ctags */
static const char default_C_help [] =
"In C code, any C function is a tag. Use -t to tag typedefs.\n\
-Use -T to tag definitions of `struct', `union' and `enum'.\n\
-Use -d to tag `#define' macro definitions and `enum' constants.\n\
+Use -T to tag definitions of 'struct', 'union' and 'enum'.\n\
+Use -d to tag '#define' macro definitions and 'enum' constants.\n\
Use --globals to tag global variables.\n\
You can tag function declarations and external variables by\n\
-using `--declarations', and struct members by using `--members'.";
+using '--declarations', and struct members by using '--members'.";
#else /* C help for Etags */
static const char default_C_help [] =
"In C code, any C function or typedef is a tag, and so are\n\
-definitions of `struct', `union' and `enum'. `#define' macro\n\
-definitions and `enum' constants are tags unless you specify\n\
-`--no-defines'. Global variables are tags unless you specify\n\
-`--no-globals' and so are struct members unless you specify\n\
-`--no-members'. Use of `--no-globals', `--no-defines' and\n\
-`--no-members' can make the tags table file much smaller.\n\
+definitions of 'struct', 'union' and 'enum'. '#define' macro\n\
+definitions and 'enum' constants are tags unless you specify\n\
+'--no-defines'. Global variables are tags unless you specify\n\
+'--no-globals' and so are struct members unless you specify\n\
+'--no-members'. Use of '--no-globals', '--no-defines' and\n\
+'--no-members' can make the tags table file much smaller.\n\
You can tag function declarations and external variables by\n\
-using `--declarations'.";
+using '--declarations'.";
#endif /* C help for Ctags and Etags */
static const char *Cplusplus_suffixes [] =
"In C++ code, all the tag constructs of C code are tagged. (Use\n\
--help --lang=c --lang=c++ for full help.)\n\
In addition to C tags, member functions are also recognized. Member\n\
-variables are recognized unless you use the `--no-members' option.\n\
-Tags for variables and functions in classes are named `CLASS::VARIABLE'\n\
-and `CLASS::FUNCTION'. `operator' definitions have tag names like\n\
-`operator+'.";
+variables are recognized unless you use the '--no-members' option.\n\
+Tags for variables and functions in classes are named 'CLASS::VARIABLE'\n\
+and 'CLASS::FUNCTION'. 'operator' definitions have tag names like\n\
+'operator+'.";
static const char *Cjava_suffixes [] =
{ "java", NULL };
const char *Forth_suffixes [] =
{ "fth", "tok", NULL };
static const char Forth_help [] =
-"In Forth code, tags are words defined by `:',\n\
+"In Forth code, tags are words defined by ':',\n\
constant, code, create, defer, value, variable, buffer:, field.";
static const char *Fortran_suffixes [] =
static const char Fortran_help [] =
"In Fortran code, functions, subroutines and block data are tags.";
+static const char *Go_suffixes [] = {"go", NULL};
+static const char Go_help [] =
+ "In Go code, functions, interfaces and packages are tags.";
+
static const char *HTML_suffixes [] =
{ "htm", "html", "shtml", NULL };
static const char HTML_help [] =
-"In HTML input files, the tags are the `title' and the `h1', `h2',\n\
-`h3' headers. Also, tags are `name=' in anchors and all\n\
-occurrences of `id='.";
+"In HTML input files, the tags are the 'title' and the 'h1', 'h2',\n\
+'h3' headers. Also, tags are 'name=' in anchors and all\n\
+occurrences of 'id='.";
static const char *Lisp_suffixes [] =
{ "cl", "clisp", "el", "l", "lisp", "LSP", "lsp", "ml", NULL };
static const char Lisp_help [] =
-"In Lisp code, any function defined with `defun', any variable\n\
-defined with `defvar' or `defconst', and in general the first\n\
-argument of any expression that starts with `(def' in column zero\n\
+"In Lisp code, any function defined with 'defun', any variable\n\
+defined with 'defvar' or 'defconst', and in general the first\n\
+argument of any expression that starts with '(def' in column zero\n\
is a tag.\n\
-The `--declarations' option tags \"(defvar foo)\" constructs too.";
+The '--declarations' option tags \"(defvar foo)\" constructs too.";
static const char *Lua_suffixes [] =
{ "lua", "LUA", NULL };
{ "Makefile", "makefile", "GNUMakefile", "Makefile.in", "Makefile.am", NULL};
static const char Makefile_help [] =
"In makefiles, targets are tags; additionally, variables are tags\n\
-unless you specify `--no-globals'.";
+unless you specify '--no-globals'.";
static const char *Objc_suffixes [] =
{ "lm", /* Objective lex file */
static const char Objc_help [] =
"In Objective C code, tags include Objective C definitions for classes,\n\
class categories, methods and protocols. Tags for variables and\n\
-functions in classes are named `CLASS::VARIABLE' and `CLASS::FUNCTION'.\n\
-(Use --help --lang=c --lang=objc --lang=java for full help.)";
+functions in classes are named 'CLASS::VARIABLE' and 'CLASS::FUNCTION'.\
+\n(Use --help --lang=c --lang=objc --lang=java for full help.)";
static const char *Pascal_suffixes [] =
{ "p", "pas", NULL };
{ "perl", "@PERL@", NULL };
static const char Perl_help [] =
"In Perl code, the tags are the packages, subroutines and variables\n\
-defined by the `package', `sub', `my' and `local' keywords. Use\n\
-`--globals' if you want to tag global variables. Tags for\n\
-subroutines are named `PACKAGE::SUB'. The name for subroutines\n\
-defined in the default package is `main::SUB'.";
+defined by the 'package', 'sub', 'my' and 'local' keywords. Use\n\
+'--globals' if you want to tag global variables. Tags for\n\
+subroutines are named 'PACKAGE::SUB'. The name for subroutines\n\
+defined in the default package is 'main::SUB'.";
static const char *PHP_suffixes [] =
{ "php", "php3", "php4", NULL };
static const char PHP_help [] =
"In PHP code, tags are functions, classes and defines. Unless you use\n\
-the `--no-members' option, vars are tags too.";
+the '--no-members' option, vars are tags too.";
static const char *plain_C_suffixes [] =
{ "pc", /* Pro*C file */
static const char *Python_suffixes [] =
{ "py", NULL };
static const char Python_help [] =
-"In Python code, `def' or `class' at the beginning of a line\n\
+"In Python code, 'def' or 'class' at the beginning of a line\n\
generate a tag.";
+static const char *Ruby_suffixes [] =
+ { "rb", "ru", "rbw", NULL };
+static const char *Ruby_filenames [] =
+ { "Rakefile", "Thorfile", NULL };
+static const char Ruby_help [] =
+ "In Ruby code, 'def' or 'class' or 'module' at the beginning of\n\
+a line generate a tag. Constants also generate a tag.";
+
/* Can't do the `SCM' or `scm' prefix with a version number. */
static const char *Scheme_suffixes [] =
{ "oak", "sch", "scheme", "SCM", "scm", "SM", "sm", "ss", "t", NULL };
static const char Scheme_help [] =
-"In Scheme code, tags include anything defined with `def' or with a\n\
-construct whose name starts with `def'. They also include\n\
-variables set with `set!' at top level in the file.";
+"In Scheme code, tags include anything defined with 'def' or with a\n\
+construct whose name starts with 'def'. They also include\n\
+variables set with 'set!' at top level in the file.";
static const char *TeX_suffixes [] =
{ "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL };
static const char TeX_help [] =
-"In LaTeX text, the argument of any of the commands `\\chapter',\n\
-`\\section', `\\subsection', `\\subsubsection', `\\eqno', `\\label',\n\
-`\\ref', `\\cite', `\\bibitem', `\\part', `\\appendix', `\\entry',\n\
-`\\index', `\\def', `\\newcommand', `\\renewcommand',\n\
-`\\newenvironment' or `\\renewenvironment' is a tag.\n\
+"In LaTeX text, the argument of any of the commands '\\chapter',\n\
+'\\section', '\\subsection', '\\subsubsection', '\\eqno', '\\label',\n\
+'\\ref', '\\cite', '\\bibitem', '\\part', '\\appendix', '\\entry',\n\
+'\\index', '\\def', '\\newcommand', '\\renewcommand',\n\
+'\\newenvironment' or '\\renewenvironment' is a tag.\n\
\n\
Other commands can be specified by setting the environment variable\n\
-`TEXTAGS' to a colon-separated list like, for example,\n\
+'TEXTAGS' to a colon-separated list like, for example,\n\
TEXTAGS=\"mycommand:myothercommand\".";
for full help).";
static const char auto_help [] =
-"`auto' is not a real language, it indicates to use\n\
+"'auto' is not a real language, it indicates to use\n\
a default language for files base on file name suffix and file contents.";
static const char none_help [] =
-"`none' is not a real language, it indicates to only do\n\
+"'none' is not a real language, it indicates to only do\n\
regexp processing on files.";
static const char no_lang_help [] =
{ "erlang", Erlang_help, Erlang_functions, Erlang_suffixes },
{ "forth", Forth_help, Forth_words, Forth_suffixes },
{ "fortran", Fortran_help, Fortran_functions, Fortran_suffixes },
+ { "go", Go_help, Go_functions, Go_suffixes },
{ "html", HTML_help, HTML_labels, HTML_suffixes },
{ "java", Cjava_help, Cjava_entries, Cjava_suffixes },
{ "lisp", Lisp_help, Lisp_functions, Lisp_suffixes },
{ "proc", no_lang_help, plain_C_entries, plain_C_suffixes },
{ "prolog", Prolog_help, Prolog_functions, Prolog_suffixes },
{ "python", Python_help, Python_functions, Python_suffixes },
+ { "ruby", Ruby_help,Ruby_functions,Ruby_suffixes,Ruby_filenames },
{ "scheme", Scheme_help, Scheme_functions, Scheme_suffixes },
{ "tex", TeX_help, TeX_commands, TeX_suffixes },
{ "texinfo", Texinfo_help, Texinfo_nodes, Texinfo_suffixes },
printf (" .%s", *ext);
puts ("");
}
- puts ("where `auto' means use default language for files based on file\n\
-name suffix, and `none' means only do regexp processing on files.\n\
+ puts ("where 'auto' means use default language for files based on file\n\
+name suffix, and 'none' means only do regexp processing on files.\n\
If no language is specified and no matching suffix is found,\n\
the first line of the file is read for a sharp-bang (#!) sequence\n\
followed by the name of an interpreter. If no such sequence is found,\n\
Do not create tag entries for global variables in some\n\
languages. This makes the tags file smaller.");
- if (PRINT_UNDOCUMENTED_OPTIONS_HELP)
- puts ("--no-line-directive\n\
+ puts ("--no-line-directive\n\
Ignore #line preprocessor directives in C and derived languages.");
if (CTAGS)
Do not create tag entries for members of structures\n\
in some languages.");
+ puts ("-Q, --class-qualify\n\
+ Qualify tag names with their class name in C++, ObjC, Java, and Perl.\n\
+ This produces tag names of the form \"class::member\" for C++,\n\
+ \"class(category)\" for Objective C, and \"class.member\" for Java.\n\
+ For Objective C, this also produces class methods qualified with\n\
+ their arguments, as in \"foo:bar:baz:more\".\n\
+ For Perl, this produces \"package::member\".");
puts ("-r REGEXP, --regex=REGEXP or --regex=@regexfile\n\
Make a tag for each line matching a regular expression pattern\n\
in the following files. {LANGUAGE}REGEXP uses REGEXP for LANGUAGE\n\
puts (" If TAGNAME/ is present, the tags created are named.\n\
For example Tcl named tags can be created with:\n\
--regex=\"/proc[ \\t]+\\([^ \\t]+\\)/\\1/.\".\n\
- MODS are optional one-letter modifiers: `i' means to ignore case,\n\
- `m' means to allow multi-line matches, `s' implies `m' and\n\
+ MODS are optional one-letter modifiers: 'i' means to ignore case,\n\
+ 'm' means to allow multi-line matches, 's' implies 'm' and\n\
causes dot to match any character, including newline.");
puts ("-R, --no-regex\n\
Print the version of the program.\n\
-h, --help\n\
Print this help message.\n\
- Followed by one or more `--language' options prints detailed\n\
+ Followed by one or more '--language' options prints detailed\n\
help about tag generation for the specified languages.");
print_language_names ();
/* When the optstring begins with a '-' getopt_long does not rearrange the
non-options arguments to be at the end, but leaves them alone. */
- optstring = concat ("-ac:Cf:Il:o:r:RSVhH",
+ optstring = concat ("-ac:Cf:Il:o:Qr:RSVhH",
(CTAGS) ? "BxdtTuvw" : "Di:",
"");
++current_arg;
++file_count;
if (parsing_stdin)
- fatal ("cannot parse standard input more than once", (char *)NULL);
+ fatal ("cannot parse standard input more than once");
parsing_stdin = true;
break;
case 'H':
help_asked = true;
break;
+ case 'Q':
+ class_qualify = 1;
+ break;
/* Etags options */
case 'D': constantypedefs = false; break;
if (streq (this_file, "-"))
{
if (parsing_stdin)
- fatal ("cannot parse standard input AND read file names from it",
- (char *)NULL);
+ fatal ("cannot parse standard input "
+ "AND read file names from it");
while (readline_internal (&filename_lb, stdin, "-") > 0)
process_file_name (filename_lb.buffer, lang);
}
this_file = argbuffer[i].what;
process_file (stdin, this_file, lang);
break;
+ default:
+ error ("internal error: arg_type");
}
}
z = stpcpy (z, tagfile);
strcpy (z, ";rm OTAGS");
if (system (cmd) != EXIT_SUCCESS)
- fatal ("failed to execute shell command", (char *)NULL);
+ fatal ("failed to execute shell command");
}
free (cmd);
append_to_tagfile = true;
{
language *lang;
const char **name, **ext, *suffix;
+ char *slash;
/* Try whole file name first. */
+ slash = strrchr (file, '/');
+ if (slash != NULL)
+ file = slash + 1;
+#ifdef DOS_NT
+ else if (file[0] && file[1] == ':')
+ file += 2;
+#endif
for (lang = lang_names; lang->name != NULL; lang++)
if (lang->filenames != NULL)
for (name = lang->filenames; *name != NULL; name++)
inf = NULL;
else
{
+#if MSDOS || defined (DOS_NT)
+ char *cmd1 = concat (compr->command, " \"", real_name);
+ char *cmd = concat (cmd1, "\" > ", tmp_name);
+#else
char *cmd1 = concat (compr->command, " '", real_name);
char *cmd = concat (cmd1, "' > ", tmp_name);
+#endif
free (cmd1);
int tmp_errno;
if (system (cmd) == -1)
case st_C_struct:
case st_C_enum:
typdef = ttypeseen;
+ break;
+ default:
+ break;
}
break;
case ttypeseen:
case st_C_struct:
case st_C_enum:
return false;
+ default:
+ return true;
}
- return true;
+ default:
+ break;
}
switch (toktype)
fvdef = fvnone;
}
return false;
+ default:
+ break;
}
if (structdef == skeyseen)
case st_C_objimpl:
objdef = oimplementation;
return false;
+ default:
+ break;
}
break;
case oimplementation:
case omethodparm:
if (parlev == 0)
{
- int oldlen = token_name.len;
- fvdef = fvnone;
objdef = omethodtag;
- linebuffer_setlen (&token_name, oldlen + len);
- memcpy (token_name.buffer + oldlen, str, len);
- token_name.buffer[oldlen + len] = '\0';
+ if (class_qualify)
+ {
+ int oldlen = token_name.len;
+ fvdef = fvnone;
+ linebuffer_setlen (&token_name, oldlen + len);
+ memcpy (token_name.buffer + oldlen, str, len);
+ token_name.buffer[oldlen + len] = '\0';
+ }
return true;
}
return false;
objdef = onone;
}
return false;
+ default:
+ break;
}
/* A function, variable or enum constant? */
return false;
}
break;
+ default:
+ break;
}
/* FALLTHRU */
case fvnameseen:
fvdef = fvnameseen; /* function or variable */
*is_func_or_var = true;
return true;
+ default:
+ break;
}
break;
+ default:
+ break;
}
return false;
&& nestlev > 0 && definedef == dnone)
/* in struct body */
{
- int len;
- write_classname (&token_name, qualifier);
- len = token_name.len;
- linebuffer_setlen (&token_name, len+qlen+toklen);
- sprintf (token_name.buffer + len, "%s%.*s",
- qualifier, toklen, newlb.buffer + tokoff);
+ if (class_qualify)
+ {
+ int len;
+ write_classname (&token_name, qualifier);
+ len = token_name.len;
+ linebuffer_setlen (&token_name,
+ len + qlen + toklen);
+ sprintf (token_name.buffer + len, "%s%.*s",
+ qualifier, toklen,
+ newlb.buffer + tokoff);
+ }
+ else
+ {
+ linebuffer_setlen (&token_name, toklen);
+ sprintf (token_name.buffer, "%.*s",
+ toklen, newlb.buffer + tokoff);
+ }
token.named = true;
}
else if (objdef == ocatseen)
/* Objective C category */
{
- int len = strlen (objtag) + 2 + toklen;
- linebuffer_setlen (&token_name, len);
- sprintf (token_name.buffer, "%s(%.*s)",
- objtag, toklen, newlb.buffer + tokoff);
+ if (class_qualify)
+ {
+ int len = strlen (objtag) + 2 + toklen;
+ linebuffer_setlen (&token_name, len);
+ sprintf (token_name.buffer, "%s(%.*s)",
+ objtag, toklen,
+ newlb.buffer + tokoff);
+ }
+ else
+ {
+ linebuffer_setlen (&token_name, toklen);
+ sprintf (token_name.buffer, "%.*s",
+ toklen, newlb.buffer + tokoff);
+ }
token.named = true;
}
else if (objdef == omethodtag
fvdef = fignore;
}
break;
+ default:
+ break;
}
if (structdef == stagseen && !cjava)
{
case dsharpseen:
savetoken = token;
break;
+ default:
+ break;
}
if (!yacc_rules || lp == newlb.buffer + 1)
{
break;
switch (objdef)
{
- case otagseen:
+ case otagseen:
objdef = oignore;
make_C_tag (true); /* an Objective C class */
break;
case omethodtag:
case omethodparm:
objdef = omethodcolon;
- int toklen = token_name.len;
- linebuffer_setlen (&token_name, toklen + 1);
- strcpy (token_name.buffer + toklen, ":");
+ if (class_qualify)
+ {
+ int toklen = token_name.len;
+ linebuffer_setlen (&token_name, toklen + 1);
+ strcpy (token_name.buffer + toklen, ":");
+ }
+ break;
+ default:
break;
}
if (structdef == stagseen)
make_C_tag (true); /* an Objective C method */
objdef = oinbody;
break;
+ default:
+ break;
}
switch (fvdef)
{
fvdef = fvnone;
}
break;
+ default:
+ break;
}
break;
case '(':
case flistseen:
fvdef = finlist;
break;
+ default:
+ break;
}
parlev++;
break;
case finlist:
fvdef = flistseen;
break;
+ default:
+ break;
}
if (!instruct
&& (typdef == tend
switch (fvdef)
{
case flistseen:
+ if (cplpl && !class_qualify)
+ {
+ /* Remove class and namespace qualifiers from the token,
+ leaving only the method/member name. */
+ char *cc, *uqname = token_name.buffer;
+ char *tok_end = token_name.buffer + token_name.len;
+
+ for (cc = token_name.buffer; cc < tok_end; cc++)
+ {
+ if (*cc == ':' && cc[1] == ':')
+ {
+ uqname = cc + 2;
+ cc++;
+ }
+ }
+ if (uqname > token_name.buffer)
+ {
+ int uqlen = strlen (uqname);
+ linebuffer_setlen (&token_name, uqlen);
+ memmove (token_name.buffer, uqname, uqlen + 1);
+ }
+ }
make_C_tag (true); /* a function */
/* FALLTHRU */
case fignore:
bracelev = -1;
}
break;
+ default:
+ break;
}
switch (structdef)
{
structdef = snone;
make_C_tag (false); /* a struct or enum */
break;
+ default:
+ break;
}
bracelev += 1;
break;
}
}
+\f
+/*
+ * Go language support
+ * Original code by Xi Lu <lx@shellcodes.org> (2016)
+ */
+static void
+Go_functions(FILE *inf)
+{
+ char *cp, *name;
+
+ LOOP_ON_INPUT_LINES(inf, lb, cp)
+ {
+ cp = skip_spaces (cp);
+
+ if (LOOKING_AT (cp, "package"))
+ {
+ name = cp;
+ while (!notinname (*cp) && *cp != '\0')
+ cp++;
+ make_tag (name, cp - name, false, lb.buffer,
+ cp - lb.buffer + 1, lineno, linecharno);
+ }
+ else if (LOOKING_AT (cp, "func"))
+ {
+ /* Go implementation of interface, such as:
+ func (n *Integer) Add(m Integer) ...
+ skip `(n *Integer)` part.
+ */
+ if (*cp == '(')
+ {
+ while (*cp != ')')
+ cp++;
+ cp = skip_spaces (cp+1);
+ }
+
+ if (*cp)
+ {
+ name = cp;
+
+ while (!notinname (*cp))
+ cp++;
+
+ make_tag (name, cp - name, true, lb.buffer,
+ cp - lb.buffer + 1, lineno, linecharno);
+ }
+ }
+ else if (members && LOOKING_AT (cp, "type"))
+ {
+ name = cp;
+
+ /* Ignore the likes of the following:
+ type (
+ A
+ )
+ */
+ if (*cp == '(')
+ return;
+
+ while (!notinname (*cp) && *cp != '\0')
+ cp++;
+
+ make_tag (name, cp - name, false, lb.buffer,
+ cp - lb.buffer + 1, lineno, linecharno);
+ }
+ }
+}
+
\f
/*
* Ada parsing
continue; /* nothing found */
pos = strchr (sp, ':');
if (pos && pos < cp && pos[1] == ':')
- /* The name is already qualified. */
- make_tag (sp, cp - sp, true,
- lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
- else
+ {
+ /* The name is already qualified. */
+ if (!class_qualify)
+ {
+ char *q = pos + 2, *qpos;
+ while ((qpos = strchr (q, ':')) != NULL
+ && qpos < cp
+ && qpos[1] == ':')
+ q = qpos + 2;
+ sp = q;
+ }
+ make_tag (sp, cp - sp, true,
+ lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+ }
+ else if (class_qualify)
/* Qualify it. */
{
char savechar, *name;
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
free (name);
}
+ else
+ make_tag (sp, cp - sp, true,
+ lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
else if (LOOKING_AT (cp, "use constant")
|| LOOKING_AT (cp, "use constant::defer"))
}
}
+/*
+ * Ruby support
+ * Original code by Xi Lu <lx@shellcodes.org> (2015)
+ */
+static void
+Ruby_functions (FILE *inf)
+{
+ char *cp = NULL;
+ bool reader = false, writer = false, alias = false, continuation = false;
+
+ LOOP_ON_INPUT_LINES (inf, lb, cp)
+ {
+ bool is_class = false;
+ bool is_method = false;
+ char *name;
+
+ cp = skip_spaces (cp);
+ if (!continuation
+ /* Constants. */
+ && c_isalpha (*cp) && c_isupper (*cp))
+ {
+ char *bp, *colon = NULL;
+
+ name = cp;
+
+ for (cp++; c_isalnum (*cp) || *cp == '_' || *cp == ':'; cp++)
+ {
+ if (*cp == ':')
+ colon = cp;
+ }
+ if (cp > name + 1)
+ {
+ bp = skip_spaces (cp);
+ if (*bp == '=' && !(bp[1] == '=' || bp[1] == '>'))
+ {
+ if (colon && !c_isspace (colon[1]))
+ name = colon + 1;
+ make_tag (name, cp - name, false,
+ lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+ }
+ }
+ }
+ else if (!continuation
+ /* Modules, classes, methods. */
+ && ((is_method = LOOKING_AT (cp, "def"))
+ || (is_class = LOOKING_AT (cp, "class"))
+ || LOOKING_AT (cp, "module")))
+ {
+ const char self_name[] = "self.";
+ const size_t self_size1 = sizeof (self_name) - 1;
+
+ name = cp;
+
+ /* Ruby method names can end in a '='. Also, operator overloading can
+ define operators whose names include '='. */
+ while (!notinname (*cp) || *cp == '=')
+ cp++;
+
+ /* Remove "self." from the method name. */
+ if (cp - name > self_size1
+ && strneq (name, self_name, self_size1))
+ name += self_size1;
+
+ /* Remove the class/module qualifiers from method names. */
+ if (is_method)
+ {
+ char *q;
+
+ for (q = name; q < cp && *q != '.'; q++)
+ ;
+ if (q < cp - 1) /* punt if we see just "FOO." */
+ name = q + 1;
+ }
+
+ /* Don't tag singleton classes. */
+ if (is_class && strneq (name, "<<", 2) && cp == name + 2)
+ continue;
+
+ make_tag (name, cp - name, true,
+ lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+ }
+ else
+ {
+ /* Tag accessors and aliases. */
+
+ if (!continuation)
+ reader = writer = alias = false;
+
+ while (*cp && *cp != '#')
+ {
+ if (!continuation)
+ {
+ reader = writer = alias = false;
+ if (LOOKING_AT (cp, "attr_reader"))
+ reader = true;
+ else if (LOOKING_AT (cp, "attr_writer"))
+ writer = true;
+ else if (LOOKING_AT (cp, "attr_accessor"))
+ {
+ reader = true;
+ writer = true;
+ }
+ else if (LOOKING_AT (cp, "alias_method"))
+ alias = true;
+ }
+ if (reader || writer || alias)
+ {
+ do {
+ char *np;
+
+ cp = skip_spaces (cp);
+ if (*cp == '(')
+ cp = skip_spaces (cp + 1);
+ np = cp;
+ cp = skip_name (cp);
+ if (*np != ':')
+ continue;
+ np++;
+ if (reader)
+ {
+ make_tag (np, cp - np, true,
+ lb.buffer, cp - lb.buffer + 1,
+ lineno, linecharno);
+ continuation = false;
+ }
+ if (writer)
+ {
+ size_t name_len = cp - np + 1;
+ char *wr_name = xnew (name_len + 1, char);
+
+ memcpy (wr_name, np, name_len - 1);
+ memcpy (wr_name + name_len - 1, "=", 2);
+ pfnote (wr_name, true, lb.buffer, cp - lb.buffer + 1,
+ lineno, linecharno);
+ continuation = false;
+ }
+ if (alias)
+ {
+ if (!continuation)
+ make_tag (np, cp - np, true,
+ lb.buffer, cp - lb.buffer + 1,
+ lineno, linecharno);
+ continuation = false;
+ while (*cp && *cp != '#' && *cp != ';')
+ {
+ if (*cp == ',')
+ continuation = true;
+ else if (!c_isspace (*cp))
+ continuation = false;
+ cp++;
+ }
+ if (*cp == ';')
+ continuation = false;
+ }
+ cp = skip_spaces (cp);
+ } while ((alias
+ ? (*cp == ',')
+ : (continuation = (*cp == ',')))
+ && (cp = skip_spaces (cp + 1), *cp && *cp != '#'));
+ }
+ if (*cp != '#')
+ cp = skip_name (cp);
+ while (*cp && *cp != '#' && notinname (*cp))
+ cp++;
+ }
+ }
+ }
+}
+
\f
/*
* PHP support
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
+ bp = skip_spaces (bp);
if (bp[0] != 'f' && bp[0] != 'l')
continue;
(void)LOOKING_AT (bp, "local"); /* skip possible "local" */
if (LOOKING_AT (bp, "function"))
- get_tag (bp, NULL);
+ {
+ char *tag_name, *tp_dot, *tp_colon;
+
+ get_tag (bp, &tag_name);
+ /* If the tag ends with ".foo" or ":foo", make an additional tag for
+ "foo". */
+ tp_dot = strrchr (tag_name, '.');
+ tp_colon = strrchr (tag_name, ':');
+ if (tp_dot || tp_colon)
+ {
+ char *p = tp_dot > tp_colon ? tp_dot : tp_colon;
+ int len_add = p - tag_name + 1;
+
+ get_tag (bp + len_add, NULL);
+ }
+ }
}
}
:part:appendix:entry:index:def\
:newcommand:renewcommand:newenvironment:renewenvironment";
-static void TEX_mode (FILE *);
static void TEX_decode_env (const char *, const char *);
-static char TEX_esc = '\\';
-static char TEX_opgrp = '{';
-static char TEX_clgrp = '}';
-
/*
* TeX/LaTeX scanning loop.
*/
char *cp;
linebuffer *key;
- /* Select either \ or ! as escape character. */
- TEX_mode (inf);
+ char TEX_esc = '\0';
+ char TEX_opgrp, TEX_clgrp;
/* Initialize token table once from environment. */
if (TEX_toktab == NULL)
for (;;)
{
/* Look for a TEX escape. */
- while (*cp++ != TEX_esc)
- if (cp[-1] == '\0' || cp[-1] == '%')
- goto tex_next_line;
+ while (true)
+ {
+ char c = *cp++;
+ if (c == '\0' || c == '%')
+ goto tex_next_line;
+
+ /* Select either \ or ! as escape character, whichever comes
+ first outside a comment. */
+ if (!TEX_esc)
+ switch (c)
+ {
+ case '\\':
+ TEX_esc = c;
+ TEX_opgrp = '{';
+ TEX_clgrp = '}';
+ break;
+
+ case '!':
+ TEX_esc = c;
+ TEX_opgrp = '<';
+ TEX_clgrp = '>';
+ break;
+ }
+
+ if (c == TEX_esc)
+ break;
+ }
for (key = TEX_toktab; key->buffer != NULL; key++)
if (strneq (cp, key->buffer, key->len))
}
}
-#define TEX_LESC '\\'
-#define TEX_SESC '!'
-
-/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
- chars accordingly. */
-static void
-TEX_mode (FILE *inf)
-{
- int c;
-
- while ((c = getc (inf)) != EOF)
- {
- /* Skip to next line if we hit the TeX comment char. */
- if (c == '%')
- while (c != '\n' && c != EOF)
- c = getc (inf);
- else if (c == TEX_LESC || c == TEX_SESC )
- break;
- }
-
- if (c == TEX_LESC)
- {
- TEX_esc = TEX_LESC;
- TEX_opgrp = '{';
- TEX_clgrp = '}';
- }
- else
- {
- TEX_esc = TEX_SESC;
- TEX_opgrp = '<';
- TEX_clgrp = '>';
- }
- reset_input (inf);
-}
-
/* Read environment and prepend it to the default string.
Build token table. */
static void
need_filebuf = true;
break;
default:
- error ("invalid regexp modifier `%c', ignoring", modifiers[0]);
+ error ("invalid regexp modifier '%c', ignoring", modifiers[0]);
break;
}
if (p > buffer && p[-1] == '\r')
{
p -= 1;
-#ifdef DOS_NT
- /* Assume CRLF->LF translation will be performed by Emacs
- when loading this file, so CRs won't appear in the buffer.
- It would be cleaner to compensate within Emacs;
- however, Emacs does not know how many CRs were deleted
- before any given point in the file. */
- chars_deleted = 1;
-#else
chars_deleted = 2;
-#endif
}
else
{
}
/* Print error message and exit. */
-void
-fatal (const char *s1, const char *s2)
+static void
+fatal (char const *format, ...)
{
- error (s1, s2);
+ va_list ap;
+ va_start (ap, format);
+ verror (format, ap);
+ va_end (ap);
exit (EXIT_FAILURE);
}
static void
suggest_asking_for_help (void)
{
- fprintf (stderr, "\tTry `%s --help' for a complete list of options.\n",
+ fprintf (stderr, "\tTry '%s --help' for a complete list of options.\n",
progname);
exit (EXIT_FAILURE);
}
{
va_list ap;
va_start (ap, format);
+ verror (format, ap);
+ va_end (ap);
+}
+
+static void
+verror (char const *format, va_list ap)
+{
fprintf (stderr, "%s: ", progname);
vfprintf (stderr, format, ap);
fprintf (stderr, "\n");
- va_end (ap);
}
/* Return a newly-allocated string whose contents
{
void *result = malloc (size);
if (result == NULL)
- fatal ("virtual memory exhausted", (char *)NULL);
+ fatal ("virtual memory exhausted");
return result;
}
{
void *result = realloc (ptr, size);
if (result == NULL)
- fatal ("virtual memory exhausted", (char *)NULL);
+ fatal ("virtual memory exhausted");
return result;
}