]> code.delx.au - gnu-emacs/blobdiff - lib-src/etags.c
Don't treat JS spread as contination method call
[gnu-emacs] / lib-src / etags.c
index 48d2299410ac7b7feff396512abf516fe27f538e..01e230206ac8419e6b9e31e03f00955a88b39f06 100644 (file)
@@ -28,15 +28,15 @@ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 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
@@ -150,10 +150,29 @@ char pot_etags_version[] = "@(#) pot revision number is 17.38.1.4";
 # 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
@@ -335,6 +354,7 @@ static void Cstar_entries (FILE *);
 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 *);
@@ -345,6 +365,7 @@ static void PHP_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 *);
@@ -361,8 +382,9 @@ static void analyze_regex (char *);
 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 **);
 
@@ -441,6 +463,7 @@ static bool cxref_style;    /* -x: create cxref style output */
 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
@@ -468,6 +491,7 @@ static struct option longopts[] =
   { "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'   },
@@ -513,7 +537,7 @@ static const char *Ada_suffixes [] =
   { "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\
@@ -524,9 +548,9 @@ Ada tag names have suffixes indicating the type of entity:\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 [] =
@@ -553,22 +577,22 @@ static const char *default_C_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 [] =
@@ -580,10 +604,10 @@ static const char Cplusplus_help [] =
 "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 };
@@ -610,7 +634,7 @@ defined in the file.";
 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 [] =
@@ -618,21 +642,25 @@ 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 };
@@ -643,7 +671,7 @@ static const char *Makefile_filenames [] =
   { "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 */
@@ -652,8 +680,8 @@ static const char *Objc_suffixes [] =
 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 };
@@ -668,16 +696,16 @@ static const char *Perl_interpreters [] =
   { "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 */
@@ -697,28 +725,36 @@ line.";
 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\".";
 
 
@@ -736,11 +772,11 @@ C code are parsed as C code (use --help --lang=c --lang=yacc\n\
 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 [] =
@@ -765,6 +801,7 @@ static language lang_names [] =
   { "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      },
@@ -778,6 +815,7 @@ static language lang_names [] =
   { "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   },
@@ -807,8 +845,8 @@ default file names and dot 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\
@@ -921,8 +959,7 @@ Relative ones are stored relative to the output file's directory.\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)
@@ -933,6 +970,13 @@ Relative ones are stored relative to the output file's directory.\n");
        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\
@@ -942,8 +986,8 @@ Relative ones are stored relative to the output file's directory.\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\
@@ -1004,7 +1048,7 @@ Relative ones are stored relative to the output file's directory.\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 ();
@@ -1050,7 +1094,7 @@ main (int argc, char **argv)
 
   /* 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:",
                      "");
 
@@ -1083,7 +1127,7 @@ main (int argc, char **argv)
        ++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;
 
@@ -1139,6 +1183,9 @@ main (int argc, char **argv)
       case 'H':
        help_asked = true;
        break;
+      case 'Q':
+       class_qualify = 1;
+       break;
 
        /* Etags options */
       case 'D': constantypedefs = false;                       break;
@@ -1244,8 +1291,8 @@ main (int argc, char **argv)
              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);
                }
@@ -1256,6 +1303,8 @@ main (int argc, char **argv)
           this_file = argbuffer[i].what;
           process_file (stdin, this_file, lang);
           break;
+       default:
+         error ("internal error: arg_type");
        }
     }
 
@@ -1313,7 +1362,7 @@ main (int argc, char **argv)
          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;
@@ -1437,8 +1486,16 @@ get_language_from_filename (char *file, int case_sensitive)
 {
   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++)
@@ -1565,8 +1622,13 @@ process_file_name (char *file, language *lang)
        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)
@@ -2737,6 +2799,9 @@ consider_token (char *str, int len, int c, int *c_extp,
         case st_C_struct:
         case st_C_enum:
           typdef = ttypeseen;
+          break;
+        default:
+          break;
         }
        break;
      case ttypeseen:
@@ -2753,8 +2818,11 @@ consider_token (char *str, int len, int c, int *c_extp,
         case st_C_struct:
         case st_C_enum:
           return false;
+        default:
+          return true;
         }
-       return true;
+     default:
+       break;
      }
 
    switch (toktype)
@@ -2787,6 +2855,8 @@ consider_token (char *str, int len, int c, int *c_extp,
             fvdef = fvnone;
         }
        return false;
+     default:
+       break;
      }
 
    if (structdef == skeyseen)
@@ -2810,6 +2880,8 @@ consider_token (char *str, int len, int c, int *c_extp,
         case st_C_objimpl:
           objdef = oimplementation;
           return false;
+        default:
+          break;
         }
        break;
      case oimplementation:
@@ -2847,12 +2919,15 @@ consider_token (char *str, int len, int c, int *c_extp,
      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;
@@ -2868,6 +2943,8 @@ consider_token (char *str, int len, int c, int *c_extp,
           objdef = onone;
         }
        return false;
+     default:
+       break;
      }
 
    /* A function, variable or enum constant? */
@@ -2923,6 +3000,8 @@ consider_token (char *str, int len, int c, int *c_extp,
                   return false;
                 }
               break;
+            default:
+              break;
             }
          /* FALLTHRU */
          case fvnameseen:
@@ -2939,8 +3018,12 @@ consider_token (char *str, int len, int c, int *c_extp,
          fvdef = fvnameseen;   /* function or variable */
          *is_func_or_var = true;
          return true;
+        default:
+          break;
        }
       break;
+     default:
+       break;
     }
 
   return false;
@@ -3302,21 +3385,42 @@ C_entries (int c_ext, FILE *inf)
                              && 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
@@ -3429,6 +3533,8 @@ C_entries (int c_ext, FILE *inf)
                          fvdef = fignore;
                        }
                      break;
+                   default:
+                     break;
                    }
                  if (structdef == stagseen && !cjava)
                    {
@@ -3439,6 +3545,8 @@ C_entries (int c_ext, FILE *inf)
                case dsharpseen:
                  savetoken = token;
                  break;
+               default:
+                 break;
                }
              if (!yacc_rules || lp == newlb.buffer + 1)
                {
@@ -3467,16 +3575,21 @@ C_entries (int c_ext, FILE *inf)
            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)
@@ -3555,6 +3668,8 @@ C_entries (int c_ext, FILE *inf)
              make_C_tag (true); /* an Objective C method */
              objdef = oinbody;
              break;
+           default:
+             break;
            }
          switch (fvdef)
            {
@@ -3628,6 +3743,8 @@ C_entries (int c_ext, FILE *inf)
                  fvdef = fvnone;
                }
              break;
+           default:
+             break;
            }
          break;
        case '(':
@@ -3661,6 +3778,8 @@ C_entries (int c_ext, FILE *inf)
            case flistseen:
              fvdef = finlist;
              break;
+           default:
+             break;
            }
          parlev++;
          break;
@@ -3686,6 +3805,8 @@ C_entries (int c_ext, FILE *inf)
                case finlist:
                  fvdef = flistseen;
                  break;
+               default:
+                 break;
                }
              if (!instruct
                  && (typdef == tend
@@ -3711,6 +3832,28 @@ C_entries (int c_ext, FILE *inf)
          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:
@@ -3735,6 +3878,8 @@ C_entries (int c_ext, FILE *inf)
                    bracelev = -1;
                }
              break;
+           default:
+             break;
            }
          switch (structdef)
            {
@@ -3748,6 +3893,8 @@ C_entries (int c_ext, FILE *inf)
              structdef = snone;
              make_C_tag (false);  /* a struct or enum */
              break;
+           default:
+             break;
            }
          bracelev += 1;
          break;
@@ -4077,6 +4224,73 @@ Fortran_functions (FILE *inf)
     }
 }
 
+\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
@@ -4321,10 +4535,21 @@ Perl_functions (FILE *inf)
            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;
@@ -4337,6 +4562,9 @@ Perl_functions (FILE *inf)
                        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"))
@@ -4409,6 +4637,175 @@ Python_functions (FILE *inf)
     }
 }
 
+/*
+ * 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
@@ -4825,13 +5222,29 @@ Lua_functions (FILE *inf)
 
   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);
+           }
+       }
     }
 }
 
@@ -4951,13 +5364,8 @@ static const char *TEX_defenv = "\
 :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.
  */
@@ -4967,8 +5375,8 @@ TeX_commands (FILE *inf)
   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)
@@ -4980,9 +5388,33 @@ TeX_commands (FILE *inf)
       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))
@@ -5020,41 +5452,6 @@ TeX_commands (FILE *inf)
     }
 }
 
-#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
@@ -5748,7 +6145,7 @@ add_regex (char *regexp_pattern, language *lang)
        need_filebuf = true;
        break;
       default:
-       error ("invalid regexp modifier `%c', ignoring", modifiers[0]);
+       error ("invalid regexp modifier '%c', ignoring", modifiers[0]);
        break;
       }
 
@@ -6026,16 +6423,7 @@ readline_internal (linebuffer *lbp, FILE *stream, char const *filename)
          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
            {
@@ -6311,10 +6699,13 @@ skip_name (char *cp)
 }
 
 /* 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);
 }
 
@@ -6328,7 +6719,7 @@ pfatal (const char *s1)
 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);
 }
@@ -6339,10 +6730,16 @@ error (const char *format, ...)
 {
   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
@@ -6633,7 +7030,7 @@ xmalloc (size_t size)
 {
   void *result = malloc (size);
   if (result == NULL)
-    fatal ("virtual memory exhausted", (char *)NULL);
+    fatal ("virtual memory exhausted");
   return result;
 }
 
@@ -6642,7 +7039,7 @@ xrealloc (void *ptr, size_t size)
 {
   void *result = realloc (ptr, size);
   if (result == NULL)
-    fatal ("virtual memory exhausted", (char *)NULL);
+    fatal ("virtual memory exhausted");
   return result;
 }