Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

‎ 452 Commits   2 Branches   1 Tag   |
Index: Makefile
===================================================================
--- Makefile	(nonexistent)
+++ Makefile	(revision 5)
@@ -0,0 +1,56 @@
+
+COMPONENT_TARGETS = $(HARDWARE_NOARCH)
+
+
+include ../../../../build-system/constants.mk
+
+
+url         = $(DOWNLOAD_SERVER)/sources/packages/a/ctags
+
+versions    = 5.8
+pkgname     = ctags
+suffix      = tar.gz
+
+tarballs    = $(addsuffix .$(suffix), $(addprefix $(pkgname)-, $(versions)))
+sha1s       = $(addsuffix .sha1sum, $(tarballs))
+
+patches     = $(CURDIR)/patches/ctags-5.8-unused-attribute.patch
+
+.NOTPARALLEL: $(patches)
+
+
+BUILD_TARGETS = $(tarballs) $(sha1s) $(patches)
+
+
+include ../../../../build-system/core.mk
+
+
+.PHONY: download_clean
+
+
+$(tarballs):
+	@echo -e "\n======= Downloading source tarballs =======" ; \
+	 for tarball in $(tarballs) ; do \
+	   echo "$(url)/$$tarball" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & \
+	 done ; wait
+
+$(sha1s): $(tarballs)
+	@for sha in $@ ; do \
+	   echo -e "\n======= Downloading '$$sha' signature =======\n" ; \
+	   echo "$(url)/$$sha" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & wait %1 ; \
+	   touch $$sha ; \
+	   echo -e "\n======= Check the '$$sha' sha1sum =======\n" ; \
+	   sha1sum --check $$sha ; ret="$$?" ; \
+	   if [ "$$ret" == "1" ]; then \
+	     echo -e "\n======= ERROR: Bad '$$sha' sha1sum =======\n" ; \
+	     exit 1 ; \
+	   fi ; \
+	 done
+
+$(patches): $(sha1s)
+	@echo -e "\n======= Create Patches =======\n" ; \
+	 ( cd create-5.8-unused-attribute-patch ; ./create.patch.sh ) ; \
+	 echo -e "\n"
+
+download_clean:
+	@rm -f $(tarballs) $(sha1s) $(patches)
Index: create-5.8-unused-attribute-patch/create.patch.sh
===================================================================
--- create-5.8-unused-attribute-patch/create.patch.sh	(nonexistent)
+++ create-5.8-unused-attribute-patch/create.patch.sh	(revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=5.8
+
+tar --files-from=file.list -xzvf ../ctags-$VERSION.tar.gz
+mv ctags-$VERSION ctags-$VERSION-orig
+
+cp -rf ./ctags-$VERSION-new ./ctags-$VERSION
+
+diff --unified -Nr  ctags-$VERSION-orig  ctags-$VERSION > ctags-$VERSION-unused-attribute.patch
+
+mv ctags-$VERSION-unused-attribute.patch ../patches
+
+rm -rf ./ctags-$VERSION
+rm -rf ./ctags-$VERSION-orig

Property changes on: create-5.8-unused-attribute-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/c.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/c.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/c.c	(revision 5)
@@ -0,0 +1,2932 @@
+/*
+*   $Id: c.c 689 2008-12-13 21:17:36Z elliotth $
+*
+*   Copyright (c) 1996-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions for parsing and scanning C, C++ and Java
+*   source files.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"        /* must always come first */
+
+#include <string.h>
+#include <setjmp.h>
+
+#include "debug.h"
+#include "entry.h"
+#include "get.h"
+#include "keyword.h"
+#include "options.h"
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+
+/*
+*   MACROS
+*/
+
+#define activeToken(st)     ((st)->token [(int) (st)->tokenIndex])
+#define parentDecl(st)      ((st)->parent == NULL ? \
+                            DECL_NONE : (st)->parent->declaration)
+#define isType(token,t)     (boolean) ((token)->type == (t))
+#define insideEnumBody(st)  ((st)->parent == NULL ? FALSE : \
+                            (boolean) ((st)->parent->declaration == DECL_ENUM))
+#define isExternCDecl(st,c) (boolean) ((c) == STRING_SYMBOL  && \
+                    ! (st)->haveQualifyingName  && (st)->scope == SCOPE_EXTERN)
+
+#define isOneOf(c,s)        (boolean) (strchr ((s), (c)) != NULL)
+
+#define isHighChar(c)       ((c) != EOF && (unsigned char)(c) >= 0xc0)
+
+/*
+*   DATA DECLARATIONS
+*/
+
+enum { NumTokens = 3 };
+
+typedef enum eException {
+	ExceptionNone, ExceptionEOF, ExceptionFormattingError,
+	ExceptionBraceFormattingError
+} exception_t;
+
+/*  Used to specify type of keyword.
+ */
+typedef enum eKeywordId {
+	KEYWORD_NONE = -1,
+	KEYWORD_ATTRIBUTE, KEYWORD_ABSTRACT,
+	KEYWORD_BOOLEAN, KEYWORD_BYTE, KEYWORD_BAD_STATE, KEYWORD_BAD_TRANS,
+	KEYWORD_BIND, KEYWORD_BIND_VAR, KEYWORD_BIT,
+	KEYWORD_CASE, KEYWORD_CATCH, KEYWORD_CHAR, KEYWORD_CLASS, KEYWORD_CONST,
+	KEYWORD_CONSTRAINT, KEYWORD_COVERAGE_BLOCK, KEYWORD_COVERAGE_DEF,
+	KEYWORD_DEFAULT, KEYWORD_DELEGATE, KEYWORD_DELETE, KEYWORD_DO,
+	KEYWORD_DOUBLE,
+	KEYWORD_ELSE, KEYWORD_ENUM, KEYWORD_EXPLICIT, KEYWORD_EXTERN,
+	KEYWORD_EXTENDS, KEYWORD_EVENT,
+	KEYWORD_FINAL, KEYWORD_FLOAT, KEYWORD_FOR, KEYWORD_FOREACH,
+	KEYWORD_FRIEND, KEYWORD_FUNCTION,
+	KEYWORD_GOTO,
+	KEYWORD_IF, KEYWORD_IMPLEMENTS, KEYWORD_IMPORT, KEYWORD_INLINE, KEYWORD_INT,
+	KEYWORD_INOUT, KEYWORD_INPUT, KEYWORD_INTEGER, KEYWORD_INTERFACE,
+	KEYWORD_INTERNAL,
+	KEYWORD_LOCAL, KEYWORD_LONG,
+	KEYWORD_M_BAD_STATE, KEYWORD_M_BAD_TRANS, KEYWORD_M_STATE, KEYWORD_M_TRANS,
+	KEYWORD_MUTABLE,
+	KEYWORD_NAMESPACE, KEYWORD_NEW, KEYWORD_NEWCOV, KEYWORD_NATIVE,
+	KEYWORD_OPERATOR, KEYWORD_OUTPUT, KEYWORD_OVERLOAD, KEYWORD_OVERRIDE,
+	KEYWORD_PACKED, KEYWORD_PORT, KEYWORD_PACKAGE, KEYWORD_PRIVATE,
+	KEYWORD_PROGRAM, KEYWORD_PROTECTED, KEYWORD_PUBLIC,
+	KEYWORD_REGISTER, KEYWORD_RETURN,
+	KEYWORD_SHADOW, KEYWORD_STATE,
+	KEYWORD_SHORT, KEYWORD_SIGNED, KEYWORD_STATIC, KEYWORD_STRING,
+	KEYWORD_STRUCT, KEYWORD_SWITCH, KEYWORD_SYNCHRONIZED,
+	KEYWORD_TASK, KEYWORD_TEMPLATE, KEYWORD_THIS, KEYWORD_THROW,
+	KEYWORD_THROWS, KEYWORD_TRANSIENT, KEYWORD_TRANS, KEYWORD_TRANSITION,
+	KEYWORD_TRY, KEYWORD_TYPEDEF, KEYWORD_TYPENAME,
+	KEYWORD_UINT, KEYWORD_ULONG, KEYWORD_UNION, KEYWORD_UNSIGNED, KEYWORD_USHORT,
+	KEYWORD_USING,
+	KEYWORD_VIRTUAL, KEYWORD_VOID, KEYWORD_VOLATILE,
+	KEYWORD_WCHAR_T, KEYWORD_WHILE
+} keywordId;
+
+/*  Used to determine whether keyword is valid for the current language and
+ *  what its ID is.
+ */
+typedef struct sKeywordDesc {
+	const char *name;
+	keywordId id;
+	short isValid [5]; /* indicates languages for which kw is valid */
+} keywordDesc;
+
+/*  Used for reporting the type of object parsed by nextToken ().
+ */
+typedef enum eTokenType {
+	TOKEN_NONE,          /* none */
+	TOKEN_ARGS,          /* a parenthetical pair and its contents */
+	TOKEN_BRACE_CLOSE,
+	TOKEN_BRACE_OPEN,
+	TOKEN_COLON,         /* the colon character */
+	TOKEN_COMMA,         /* the comma character */
+	TOKEN_DOUBLE_COLON,  /* double colon indicates nested-name-specifier */
+	TOKEN_KEYWORD,
+	TOKEN_NAME,          /* an unknown name */
+	TOKEN_PACKAGE,       /* a Java package name */
+	TOKEN_PAREN_NAME,    /* a single name in parentheses */
+	TOKEN_SEMICOLON,     /* the semicolon character */
+	TOKEN_SPEC,          /* a storage class specifier, qualifier, type, etc. */
+	TOKEN_COUNT
+} tokenType;
+
+/*  This describes the scoping of the current statement.
+ */
+typedef enum eTagScope {
+	SCOPE_GLOBAL,        /* no storage class specified */
+	SCOPE_STATIC,        /* static storage class */
+	SCOPE_EXTERN,        /* external storage class */
+	SCOPE_FRIEND,        /* declares access only */
+	SCOPE_TYPEDEF,       /* scoping depends upon context */
+	SCOPE_COUNT
+} tagScope;
+
+typedef enum eDeclaration {
+	DECL_NONE,
+	DECL_BASE,           /* base type (default) */
+	DECL_CLASS,
+	DECL_ENUM,
+	DECL_EVENT,
+	DECL_FUNCTION,
+	DECL_IGNORE,         /* non-taggable "declaration" */
+	DECL_INTERFACE,
+	DECL_NAMESPACE,
+	DECL_NOMANGLE,       /* C++ name demangling block */
+	DECL_PACKAGE,
+	DECL_PROGRAM,        /* Vera program */
+	DECL_STRUCT,
+	DECL_TASK,           /* Vera task */
+	DECL_UNION,
+	DECL_COUNT
+} declType;
+
+typedef enum eVisibilityType {
+	ACCESS_UNDEFINED,
+	ACCESS_LOCAL,
+	ACCESS_PRIVATE,
+	ACCESS_PROTECTED,
+	ACCESS_PUBLIC,
+	ACCESS_DEFAULT,      /* Java-specific */
+	ACCESS_COUNT
+} accessType;
+
+/*  Information about the parent class of a member (if any).
+ */
+typedef struct sMemberInfo {
+	accessType access;           /* access of current statement */
+	accessType accessDefault;    /* access default for current statement */
+} memberInfo;
+
+typedef struct sTokenInfo {
+	tokenType     type;
+	keywordId     keyword;
+	vString*      name;          /* the name of the token */
+	unsigned long lineNumber;    /* line number of tag */
+	fpos_t        filePosition;  /* file position of line containing name */
+} tokenInfo;
+
+typedef enum eImplementation {
+	IMP_DEFAULT,
+	IMP_ABSTRACT,
+	IMP_VIRTUAL,
+	IMP_PURE_VIRTUAL,
+	IMP_COUNT
+} impType;
+
+/*  Describes the statement currently undergoing analysis.
+ */
+typedef struct sStatementInfo {
+	tagScope	scope;
+	declType	declaration;    /* specifier associated with TOKEN_SPEC */
+	boolean		gotName;        /* was a name parsed yet? */
+	boolean		haveQualifyingName;  /* do we have a name we are considering? */
+	boolean		gotParenName;   /* was a name inside parentheses parsed yet? */
+	boolean		gotArgs;        /* was a list of parameters parsed yet? */
+	boolean		isPointer;      /* is 'name' a pointer? */
+	boolean     inFunction;     /* are we inside of a function? */
+	boolean		assignment;     /* have we handled an '='? */
+	boolean		notVariable;    /* has a variable declaration been disqualified ? */
+	impType		implementation; /* abstract or concrete implementation? */
+	unsigned int tokenIndex;    /* currently active token */
+	tokenInfo*	token [(int) NumTokens];
+	tokenInfo*	context;        /* accumulated scope of current statement */
+	tokenInfo*	blockName;      /* name of current block */
+	memberInfo	member;         /* information regarding parent class/struct */
+	vString*	parentClasses;  /* parent classes */
+	struct sStatementInfo *parent;  /* statement we are nested within */
+} statementInfo;
+
+/*  Describes the type of tag being generated.
+ */
+typedef enum eTagType {
+	TAG_UNDEFINED,
+	TAG_CLASS,       /* class name */
+	TAG_ENUM,        /* enumeration name */
+	TAG_ENUMERATOR,  /* enumerator (enumeration value) */
+	TAG_EVENT,       /* event */
+	TAG_FIELD,       /* field (Java) */
+	TAG_FUNCTION,    /* function definition */
+	TAG_INTERFACE,   /* interface declaration */
+	TAG_LOCAL,       /* local variable definition */
+	TAG_MEMBER,      /* structure, class or interface member */
+	TAG_METHOD,      /* method declaration */
+	TAG_NAMESPACE,   /* namespace name */
+	TAG_PACKAGE,     /* package name */
+	TAG_PROGRAM,     /* program name */
+	TAG_PROPERTY,    /* property name */
+	TAG_PROTOTYPE,   /* function prototype or declaration */
+	TAG_STRUCT,      /* structure name */
+	TAG_TASK,        /* task name */
+	TAG_TYPEDEF,     /* typedef name */
+	TAG_UNION,       /* union name */
+	TAG_VARIABLE,    /* variable definition */
+	TAG_EXTERN_VAR,  /* external variable declaration */
+	TAG_COUNT        /* must be last */
+} tagType;
+
+typedef struct sParenInfo {
+	boolean isPointer;
+	boolean isParamList;
+	boolean isKnrParamList;
+	boolean isNameCandidate;
+	boolean invalidContents;
+	boolean nestedArgs;
+	unsigned int parameterCount;
+} parenInfo;
+
+/*
+*   DATA DEFINITIONS
+*/
+
+static jmp_buf Exception;
+
+static langType Lang_c;
+static langType Lang_cpp;
+static langType Lang_csharp;
+static langType Lang_java;
+static langType Lang_vera;
+static vString *Signature;
+static boolean CollectingSignature;
+
+/* Number used to uniquely identify anonymous structs and unions. */
+static int AnonymousID = 0;
+
+/* Used to index into the CKinds table. */
+typedef enum {
+	CK_UNDEFINED = -1,
+	CK_CLASS, CK_DEFINE, CK_ENUMERATOR, CK_FUNCTION,
+	CK_ENUMERATION, CK_LOCAL, CK_MEMBER, CK_NAMESPACE, CK_PROTOTYPE,
+	CK_STRUCT, CK_TYPEDEF, CK_UNION, CK_VARIABLE,
+	CK_EXTERN_VARIABLE
+} cKind;
+
+static kindOption CKinds [] = {
+	{ TRUE,  'c', "class",      "classes"},
+	{ TRUE,  'd', "macro",      "macro definitions"},
+	{ TRUE,  'e', "enumerator", "enumerators (values inside an enumeration)"},
+	{ TRUE,  'f', "function",   "function definitions"},
+	{ TRUE,  'g', "enum",       "enumeration names"},
+	{ FALSE, 'l', "local",      "local variables"},
+	{ TRUE,  'm', "member",     "class, struct, and union members"},
+	{ TRUE,  'n', "namespace",  "namespaces"},
+	{ FALSE, 'p', "prototype",  "function prototypes"},
+	{ TRUE,  's', "struct",     "structure names"},
+	{ TRUE,  't', "typedef",    "typedefs"},
+	{ TRUE,  'u', "union",      "union names"},
+	{ TRUE,  'v', "variable",   "variable definitions"},
+	{ FALSE, 'x', "externvar",  "external and forward variable declarations"},
+};
+
+typedef enum {
+	CSK_UNDEFINED = -1,
+	CSK_CLASS, CSK_DEFINE, CSK_ENUMERATOR, CSK_EVENT, CSK_FIELD,
+	CSK_ENUMERATION, CSK_INTERFACE, CSK_LOCAL, CSK_METHOD,
+	CSK_NAMESPACE, CSK_PROPERTY, CSK_STRUCT, CSK_TYPEDEF
+} csharpKind;
+
+static kindOption CsharpKinds [] = {
+	{ TRUE,  'c', "class",      "classes"},
+	{ TRUE,  'd', "macro",      "macro definitions"},
+	{ TRUE,  'e', "enumerator", "enumerators (values inside an enumeration)"},
+	{ TRUE,  'E', "event",      "events"},
+	{ TRUE,  'f', "field",      "fields"},
+	{ TRUE,  'g', "enum",       "enumeration names"},
+	{ TRUE,  'i', "interface",  "interfaces"},
+	{ FALSE, 'l', "local",      "local variables"},
+	{ TRUE,  'm', "method",     "methods"},
+	{ TRUE,  'n', "namespace",  "namespaces"},
+	{ TRUE,  'p', "property",   "properties"},
+	{ TRUE,  's', "struct",     "structure names"},
+	{ TRUE,  't', "typedef",    "typedefs"},
+};
+
+/* Used to index into the JavaKinds table. */
+typedef enum {
+	JK_UNDEFINED = -1,
+	JK_CLASS, JK_ENUM_CONSTANT, JK_FIELD, JK_ENUM, JK_INTERFACE,
+	JK_LOCAL, JK_METHOD, JK_PACKAGE, JK_ACCESS, JK_CLASS_PREFIX
+} javaKind;
+
+static kindOption JavaKinds [] = {
+	{ TRUE,  'c', "class",         "classes"},
+	{ TRUE,  'e', "enum constant", "enum constants"},
+	{ TRUE,  'f', "field",         "fields"},
+	{ TRUE,  'g', "enum",          "enum types"},
+	{ TRUE,  'i', "interface",     "interfaces"},
+	{ FALSE, 'l', "local",         "local variables"},
+	{ TRUE,  'm', "method",        "methods"},
+	{ TRUE,  'p', "package",       "packages"},
+};
+
+/* Used to index into the VeraKinds table. */
+typedef enum {
+	VK_UNDEFINED = -1,
+	VK_CLASS, VK_DEFINE, VK_ENUMERATOR, VK_FUNCTION,
+	VK_ENUMERATION, VK_LOCAL, VK_MEMBER, VK_PROGRAM, VK_PROTOTYPE,
+	VK_TASK, VK_TYPEDEF, VK_VARIABLE,
+	VK_EXTERN_VARIABLE
+} veraKind;
+
+static kindOption VeraKinds [] = {
+	{ TRUE,  'c', "class",      "classes"},
+	{ TRUE,  'd', "macro",      "macro definitions"},
+	{ TRUE,  'e', "enumerator", "enumerators (values inside an enumeration)"},
+	{ TRUE,  'f', "function",   "function definitions"},
+	{ TRUE,  'g', "enum",       "enumeration names"},
+	{ FALSE, 'l', "local",      "local variables"},
+	{ TRUE,  'm', "member",     "class, struct, and union members"},
+	{ TRUE,  'p', "program",    "programs"},
+	{ FALSE, 'P', "prototype",  "function prototypes"},
+	{ TRUE,  't', "task",       "tasks"},
+	{ TRUE,  'T', "typedef",    "typedefs"},
+	{ TRUE,  'v', "variable",   "variable definitions"},
+	{ FALSE, 'x', "externvar",  "external variable declarations"}
+};
+
+static const keywordDesc KeywordTable [] = {
+	/*                                              C++            */
+	/*                                       ANSI C  |  C# Java    */
+	/*                                            |  |  |  |  Vera */
+	/* keyword          keyword ID                |  |  |  |  |    */
+	{ "__attribute__",  KEYWORD_ATTRIBUTE,      { 1, 1, 1, 0, 0 } },
+	{ "abstract",       KEYWORD_ABSTRACT,       { 0, 0, 1, 1, 0 } },
+	{ "bad_state",      KEYWORD_BAD_STATE,      { 0, 0, 0, 0, 1 } },
+	{ "bad_trans",      KEYWORD_BAD_TRANS,      { 0, 0, 0, 0, 1 } },
+	{ "bind",           KEYWORD_BIND,           { 0, 0, 0, 0, 1 } },
+	{ "bind_var",       KEYWORD_BIND_VAR,       { 0, 0, 0, 0, 1 } },
+	{ "bit",            KEYWORD_BIT,            { 0, 0, 0, 0, 1 } },
+	{ "boolean",        KEYWORD_BOOLEAN,        { 0, 0, 0, 1, 0 } },
+	{ "byte",           KEYWORD_BYTE,           { 0, 0, 0, 1, 0 } },
+	{ "case",           KEYWORD_CASE,           { 1, 1, 1, 1, 0 } },
+	{ "catch",          KEYWORD_CATCH,          { 0, 1, 1, 0, 0 } },
+	{ "char",           KEYWORD_CHAR,           { 1, 1, 1, 1, 0 } },
+	{ "class",          KEYWORD_CLASS,          { 0, 1, 1, 1, 1 } },
+	{ "const",          KEYWORD_CONST,          { 1, 1, 1, 1, 0 } },
+	{ "constraint",     KEYWORD_CONSTRAINT,     { 0, 0, 0, 0, 1 } },
+	{ "coverage_block", KEYWORD_COVERAGE_BLOCK, { 0, 0, 0, 0, 1 } },
+	{ "coverage_def",   KEYWORD_COVERAGE_DEF,   { 0, 0, 0, 0, 1 } },
+	{ "do",             KEYWORD_DO,             { 1, 1, 1, 1, 0 } },
+	{ "default",        KEYWORD_DEFAULT,        { 1, 1, 1, 1, 0 } },
+	{ "delegate",       KEYWORD_DELEGATE,       { 0, 0, 1, 0, 0 } },
+	{ "delete",         KEYWORD_DELETE,         { 0, 1, 0, 0, 0 } },
+	{ "double",         KEYWORD_DOUBLE,         { 1, 1, 1, 1, 0 } },
+	{ "else",           KEYWORD_ELSE,           { 1, 1, 1, 1, 0 } },
+	{ "enum",           KEYWORD_ENUM,           { 1, 1, 1, 1, 1 } },
+	{ "event",          KEYWORD_EVENT,          { 0, 0, 1, 0, 1 } },
+	{ "explicit",       KEYWORD_EXPLICIT,       { 0, 1, 1, 0, 0 } },
+	{ "extends",        KEYWORD_EXTENDS,        { 0, 0, 0, 1, 1 } },
+	{ "extern",         KEYWORD_EXTERN,         { 1, 1, 1, 0, 1 } },
+	{ "final",          KEYWORD_FINAL,          { 0, 0, 0, 1, 0 } },
+	{ "float",          KEYWORD_FLOAT,          { 1, 1, 1, 1, 0 } },
+	{ "for",            KEYWORD_FOR,            { 1, 1, 1, 1, 0 } },
+	{ "foreach",        KEYWORD_FOREACH,        { 0, 0, 1, 0, 0 } },
+	{ "friend",         KEYWORD_FRIEND,         { 0, 1, 0, 0, 0 } },
+	{ "function",       KEYWORD_FUNCTION,       { 0, 0, 0, 0, 1 } },
+	{ "goto",           KEYWORD_GOTO,           { 1, 1, 1, 1, 0 } },
+	{ "if",             KEYWORD_IF,             { 1, 1, 1, 1, 0 } },
+	{ "implements",     KEYWORD_IMPLEMENTS,     { 0, 0, 0, 1, 0 } },
+	{ "import",         KEYWORD_IMPORT,         { 0, 0, 0, 1, 0 } },
+	{ "inline",         KEYWORD_INLINE,         { 0, 1, 0, 0, 0 } },
+	{ "inout",          KEYWORD_INOUT,          { 0, 0, 0, 0, 1 } },
+	{ "input",          KEYWORD_INPUT,          { 0, 0, 0, 0, 1 } },
+	{ "int",            KEYWORD_INT,            { 1, 1, 1, 1, 0 } },
+	{ "integer",        KEYWORD_INTEGER,        { 0, 0, 0, 0, 1 } },
+	{ "interface",      KEYWORD_INTERFACE,      { 0, 0, 1, 1, 1 } },
+	{ "internal",       KEYWORD_INTERNAL,       { 0, 0, 1, 0, 0 } },
+	{ "local",          KEYWORD_LOCAL,          { 0, 0, 0, 0, 1 } },
+	{ "long",           KEYWORD_LONG,           { 1, 1, 1, 1, 0 } },
+	{ "m_bad_state",    KEYWORD_M_BAD_STATE,    { 0, 0, 0, 0, 1 } },
+	{ "m_bad_trans",    KEYWORD_M_BAD_TRANS,    { 0, 0, 0, 0, 1 } },
+	{ "m_state",        KEYWORD_M_STATE,        { 0, 0, 0, 0, 1 } },
+	{ "m_trans",        KEYWORD_M_TRANS,        { 0, 0, 0, 0, 1 } },
+	{ "mutable",        KEYWORD_MUTABLE,        { 0, 1, 0, 0, 0 } },
+	{ "namespace",      KEYWORD_NAMESPACE,      { 0, 1, 1, 0, 0 } },
+	{ "native",         KEYWORD_NATIVE,         { 0, 0, 0, 1, 0 } },
+	{ "new",            KEYWORD_NEW,            { 0, 1, 1, 1, 0 } },
+	{ "newcov",         KEYWORD_NEWCOV,         { 0, 0, 0, 0, 1 } },
+	{ "operator",       KEYWORD_OPERATOR,       { 0, 1, 1, 0, 0 } },
+	{ "output",         KEYWORD_OUTPUT,         { 0, 0, 0, 0, 1 } },
+	{ "overload",       KEYWORD_OVERLOAD,       { 0, 1, 0, 0, 0 } },
+	{ "override",       KEYWORD_OVERRIDE,       { 0, 0, 1, 0, 0 } },
+	{ "package",        KEYWORD_PACKAGE,        { 0, 0, 0, 1, 0 } },
+	{ "packed",         KEYWORD_PACKED,         { 0, 0, 0, 0, 1 } },
+	{ "port",           KEYWORD_PORT,           { 0, 0, 0, 0, 1 } },
+	{ "private",        KEYWORD_PRIVATE,        { 0, 1, 1, 1, 0 } },
+	{ "program",        KEYWORD_PROGRAM,        { 0, 0, 0, 0, 1 } },
+	{ "protected",      KEYWORD_PROTECTED,      { 0, 1, 1, 1, 1 } },
+	{ "public",         KEYWORD_PUBLIC,         { 0, 1, 1, 1, 1 } },
+	{ "register",       KEYWORD_REGISTER,       { 1, 1, 0, 0, 0 } },
+	{ "return",         KEYWORD_RETURN,         { 1, 1, 1, 1, 0 } },
+	{ "shadow",         KEYWORD_SHADOW,         { 0, 0, 0, 0, 1 } },
+	{ "short",          KEYWORD_SHORT,          { 1, 1, 1, 1, 0 } },
+	{ "signed",         KEYWORD_SIGNED,         { 1, 1, 0, 0, 0 } },
+	{ "state",          KEYWORD_STATE,          { 0, 0, 0, 0, 1 } },
+	{ "static",         KEYWORD_STATIC,         { 1, 1, 1, 1, 1 } },
+	{ "string",         KEYWORD_STRING,         { 0, 0, 1, 0, 1 } },
+	{ "struct",         KEYWORD_STRUCT,         { 1, 1, 1, 0, 0 } },
+	{ "switch",         KEYWORD_SWITCH,         { 1, 1, 1, 1, 0 } },
+	{ "synchronized",   KEYWORD_SYNCHRONIZED,   { 0, 0, 0, 1, 0 } },
+	{ "task",           KEYWORD_TASK,           { 0, 0, 0, 0, 1 } },
+	{ "template",       KEYWORD_TEMPLATE,       { 0, 1, 0, 0, 0 } },
+	{ "this",           KEYWORD_THIS,           { 0, 1, 1, 1, 0 } },
+	{ "throw",          KEYWORD_THROW,          { 0, 1, 1, 1, 0 } },
+	{ "throws",         KEYWORD_THROWS,         { 0, 0, 0, 1, 0 } },
+	{ "trans",          KEYWORD_TRANS,          { 0, 0, 0, 0, 1 } },
+	{ "transition",     KEYWORD_TRANSITION,     { 0, 0, 0, 0, 1 } },
+	{ "transient",      KEYWORD_TRANSIENT,      { 0, 0, 0, 1, 0 } },
+	{ "try",            KEYWORD_TRY,            { 0, 1, 1, 0, 0 } },
+	{ "typedef",        KEYWORD_TYPEDEF,        { 1, 1, 1, 0, 1 } },
+	{ "typename",       KEYWORD_TYPENAME,       { 0, 1, 0, 0, 0 } },
+	{ "uint",           KEYWORD_UINT,           { 0, 0, 1, 0, 0 } },
+	{ "ulong",          KEYWORD_ULONG,          { 0, 0, 1, 0, 0 } },
+	{ "union",          KEYWORD_UNION,          { 1, 1, 0, 0, 0 } },
+	{ "unsigned",       KEYWORD_UNSIGNED,       { 1, 1, 1, 0, 0 } },
+	{ "ushort",         KEYWORD_USHORT,         { 0, 0, 1, 0, 0 } },
+	{ "using",          KEYWORD_USING,          { 0, 1, 1, 0, 0 } },
+	{ "virtual",        KEYWORD_VIRTUAL,        { 0, 1, 1, 0, 1 } },
+	{ "void",           KEYWORD_VOID,           { 1, 1, 1, 1, 1 } },
+	{ "volatile",       KEYWORD_VOLATILE,       { 1, 1, 1, 1, 0 } },
+	{ "wchar_t",        KEYWORD_WCHAR_T,        { 1, 1, 1, 0, 0 } },
+	{ "while",          KEYWORD_WHILE,          { 1, 1, 1, 1, 0 } }
+};
+
+/*
+*   FUNCTION PROTOTYPES
+*/
+static void createTags (const unsigned int nestLevel, statementInfo *const parent);
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+extern boolean includingDefineTags (void)
+{
+	return CKinds [CK_DEFINE].enabled;
+}
+
+/*
+*   Token management
+*/
+
+static void initToken (tokenInfo* const token)
+{
+	token->type			= TOKEN_NONE;
+	token->keyword		= KEYWORD_NONE;
+	token->lineNumber	= getSourceLineNumber ();
+	token->filePosition	= getInputFilePosition ();
+	vStringClear (token->name);
+}
+
+static void advanceToken (statementInfo* const st)
+{
+	if (st->tokenIndex >= (unsigned int) NumTokens - 1)
+		st->tokenIndex = 0;
+	else
+		++st->tokenIndex;
+	initToken (st->token [st->tokenIndex]);
+}
+
+static tokenInfo *prevToken (const statementInfo *const st, unsigned int n)
+{
+	unsigned int tokenIndex;
+	unsigned int num = (unsigned int) NumTokens;
+	Assert (n < num);
+	tokenIndex = (st->tokenIndex + num - n) % num;
+	return st->token [tokenIndex];
+}
+
+static void setToken (statementInfo *const st, const tokenType type)
+{
+	tokenInfo *token;
+	token = activeToken (st);
+	initToken (token);
+	token->type = type;
+}
+
+static void retardToken (statementInfo *const st)
+{
+	if (st->tokenIndex == 0)
+		st->tokenIndex = (unsigned int) NumTokens - 1;
+	else
+		--st->tokenIndex;
+	setToken (st, TOKEN_NONE);
+}
+
+static tokenInfo *newToken (void)
+{
+	tokenInfo *const token = xMalloc (1, tokenInfo);
+	token->name = vStringNew ();
+	initToken (token);
+	return token;
+}
+
+static void deleteToken (tokenInfo *const token)
+{
+	if (token != NULL)
+	{
+		vStringDelete (token->name);
+		eFree (token);
+	}
+}
+
+static const char *accessString (const accessType access)
+{
+	static const char *const names [] = {
+		"?", "local", "private", "protected", "public", "default"
+	};
+	Assert (sizeof (names) / sizeof (names [0]) == ACCESS_COUNT);
+	Assert ((int) access < ACCESS_COUNT);
+	return names [(int) access];
+}
+
+static const char *implementationString (const impType imp)
+{
+	static const char *const names [] ={
+		"?", "abstract", "virtual", "pure virtual"
+	};
+	Assert (sizeof (names) / sizeof (names [0]) == IMP_COUNT);
+	Assert ((int) imp < IMP_COUNT);
+	return names [(int) imp];
+}
+
+/*
+*   Debugging functions
+*/
+
+#ifdef DEBUG
+
+#define boolString(c)   ((c) ? "TRUE" : "FALSE")
+
+static const char *tokenString (const tokenType type)
+{
+	static const char *const names [] = {
+		"none", "args", "}", "{", "colon", "comma", "double colon", "keyword",
+		"name", "package", "paren-name", "semicolon", "specifier"
+	};
+	Assert (sizeof (names) / sizeof (names [0]) == TOKEN_COUNT);
+	Assert ((int) type < TOKEN_COUNT);
+	return names [(int) type];
+}
+
+static const char *scopeString (const tagScope scope)
+{
+	static const char *const names [] = {
+		"global", "static", "extern", "friend", "typedef"
+	};
+	Assert (sizeof (names) / sizeof (names [0]) == SCOPE_COUNT);
+	Assert ((int) scope < SCOPE_COUNT);
+	return names [(int) scope];
+}
+
+static const char *declString (const declType declaration)
+{
+	static const char *const names [] = {
+		"?", "base", "class", "enum", "event", "function", "ignore",
+		"interface", "namespace", "no mangle", "package", "program",
+		"struct", "task", "union",
+	};
+	Assert (sizeof (names) / sizeof (names [0]) == DECL_COUNT);
+	Assert ((int) declaration < DECL_COUNT);
+	return names [(int) declaration];
+}
+
+static const char *keywordString (const keywordId keyword)
+{
+	const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
+	const char *name = "none";
+	size_t i;
+	for (i = 0  ;  i < count  ;  ++i)
+	{
+		const keywordDesc *p = &KeywordTable [i];
+		if (p->id == keyword)
+		{
+			name = p->name;
+			break;
+		}
+	}
+	return name;
+}
+
+static void pt (tokenInfo *const token)
+{
+	if (isType (token, TOKEN_NAME))
+		printf ("type: %-12s: %-13s   line: %lu\n",
+			tokenString (token->type), vStringValue (token->name),
+			token->lineNumber);
+	else if (isType (token, TOKEN_KEYWORD))
+		printf ("type: %-12s: %-13s   line: %lu\n",
+			tokenString (token->type), keywordString (token->keyword),
+			token->lineNumber);
+	else
+		printf ("type: %-12s                  line: %lu\n",
+			tokenString (token->type), token->lineNumber);
+}
+
+static void ps (statementInfo *const st)
+{
+	unsigned int i;
+	printf ("scope: %s   decl: %s   gotName: %s   gotParenName: %s\n",
+		scopeString (st->scope), declString (st->declaration),
+		boolString (st->gotName), boolString (st->gotParenName));
+	printf ("haveQualifyingName: %s\n", boolString (st->haveQualifyingName));
+	printf ("access: %s   default: %s\n", accessString (st->member.access),
+		accessString (st->member.accessDefault));
+	printf ("token  : ");
+	pt (activeToken (st));
+	for (i = 1  ;  i < (unsigned int) NumTokens  ;  ++i)
+	{
+		printf ("prev %u : ", i);
+		pt (prevToken (st, i));
+	}
+	printf ("context: ");
+	pt (st->context);
+}
+
+#endif
+
+/*
+*   Statement management
+*/
+
+static boolean isContextualKeyword (const tokenInfo *const token)
+{
+	boolean result;
+	switch (token->keyword)
+	{
+		case KEYWORD_CLASS:
+		case KEYWORD_ENUM:
+		case KEYWORD_INTERFACE:
+		case KEYWORD_NAMESPACE:
+		case KEYWORD_STRUCT:
+		case KEYWORD_UNION:
+			result = TRUE;
+			break;
+
+		default: result = FALSE; break;
+	}
+	return result;
+}
+
+static boolean isContextualStatement (const statementInfo *const st)
+{
+	boolean result = FALSE;
+	if (st != NULL) switch (st->declaration)
+	{
+		case DECL_CLASS:
+		case DECL_ENUM:
+		case DECL_INTERFACE:
+		case DECL_NAMESPACE:
+		case DECL_STRUCT:
+		case DECL_UNION:
+			result = TRUE;
+			break;
+
+		default: result = FALSE; break;
+	}
+	return result;
+}
+
+static boolean isMember (const statementInfo *const st)
+{
+	boolean result;
+	if (isType (st->context, TOKEN_NAME))
+		result = TRUE;
+	else
+		result = (boolean)
+			(st->parent != NULL && isContextualStatement (st->parent));
+	return result;
+}
+
+static void initMemberInfo (statementInfo *const st)
+{
+	accessType accessDefault = ACCESS_UNDEFINED;
+
+	if (st->parent != NULL) switch (st->parent->declaration)
+	{
+		case DECL_ENUM:
+			accessDefault = (isLanguage (Lang_java) ? ACCESS_PUBLIC : ACCESS_UNDEFINED);
+			break;
+		case DECL_NAMESPACE:
+			accessDefault = ACCESS_UNDEFINED;
+			break;
+
+		case DECL_CLASS:
+			if (isLanguage (Lang_java))
+				accessDefault = ACCESS_DEFAULT;
+			else
+				accessDefault = ACCESS_PRIVATE;
+			break;
+
+		case DECL_INTERFACE:
+		case DECL_STRUCT:
+		case DECL_UNION:
+			accessDefault = ACCESS_PUBLIC;
+			break;
+
+		default: break;
+	}
+	st->member.accessDefault = accessDefault;
+	st->member.access		 = accessDefault;
+}
+
+static void reinitStatement (statementInfo *const st, const boolean partial)
+{
+	unsigned int i;
+
+	if (! partial)
+	{
+		st->scope = SCOPE_GLOBAL;
+		if (isContextualStatement (st->parent))
+			st->declaration = DECL_BASE;
+		else
+			st->declaration = DECL_NONE;
+	}
+	st->gotParenName	= FALSE;
+	st->isPointer		= FALSE;
+	st->inFunction		= FALSE;
+	st->assignment		= FALSE;
+	st->notVariable		= FALSE;
+	st->implementation	= IMP_DEFAULT;
+	st->gotArgs			= FALSE;
+	st->gotName			= FALSE;
+	st->haveQualifyingName = FALSE;
+	st->tokenIndex		= 0;
+
+	if (st->parent != NULL)
+		st->inFunction = st->parent->inFunction;
+
+	for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
+		initToken (st->token [i]);
+
+	initToken (st->context);
+
+	/*	Keep the block name, so that a variable following after a comma will
+	 *	still have the structure name.
+	 */
+	if (! partial)
+		initToken (st->blockName);
+
+	vStringClear (st->parentClasses);
+
+	/*  Init member info.
+	 */
+	if (! partial)
+		st->member.access = st->member.accessDefault;
+}
+
+static void initStatement (statementInfo *const st, statementInfo *const parent)
+{
+	st->parent = parent;
+	initMemberInfo (st);
+	reinitStatement (st, FALSE);
+}
+
+/*
+*   Tag generation functions
+*/
+static cKind cTagKind (const tagType type)
+{
+	cKind result = CK_UNDEFINED;
+	switch (type)
+	{
+		case TAG_CLASS:      result = CK_CLASS;       break;
+		case TAG_ENUM:       result = CK_ENUMERATION; break;
+		case TAG_ENUMERATOR: result = CK_ENUMERATOR;  break;
+		case TAG_FUNCTION:   result = CK_FUNCTION;    break;
+		case TAG_LOCAL:      result = CK_LOCAL;       break;
+		case TAG_MEMBER:     result = CK_MEMBER;      break;
+		case TAG_NAMESPACE:  result = CK_NAMESPACE;   break;
+		case TAG_PROTOTYPE:  result = CK_PROTOTYPE;   break;
+		case TAG_STRUCT:     result = CK_STRUCT;      break;
+		case TAG_TYPEDEF:    result = CK_TYPEDEF;     break;
+		case TAG_UNION:      result = CK_UNION;       break;
+		case TAG_VARIABLE:   result = CK_VARIABLE;    break;
+		case TAG_EXTERN_VAR: result = CK_EXTERN_VARIABLE; break;
+
+		default: Assert ("Bad C tag type" == NULL); break;
+	}
+	return result;
+}
+
+static csharpKind csharpTagKind (const tagType type)
+{
+	csharpKind result = CSK_UNDEFINED;
+	switch (type)
+	{
+		case TAG_CLASS:      result = CSK_CLASS;           break;
+		case TAG_ENUM:       result = CSK_ENUMERATION;     break;
+		case TAG_ENUMERATOR: result = CSK_ENUMERATOR;      break;
+		case TAG_EVENT:      result = CSK_EVENT;           break;
+		case TAG_FIELD:      result = CSK_FIELD ;          break;
+		case TAG_INTERFACE:  result = CSK_INTERFACE;       break;
+		case TAG_LOCAL:      result = CSK_LOCAL;           break;
+		case TAG_METHOD:     result = CSK_METHOD;          break;
+		case TAG_NAMESPACE:  result = CSK_NAMESPACE;       break;
+		case TAG_PROPERTY:   result = CSK_PROPERTY;        break;
+		case TAG_STRUCT:     result = CSK_STRUCT;          break;
+		case TAG_TYPEDEF:    result = CSK_TYPEDEF;         break;
+
+		default: Assert ("Bad C# tag type" == NULL); break;
+	}
+	return result;
+}
+
+static javaKind javaTagKind (const tagType type)
+{
+	javaKind result = JK_UNDEFINED;
+	switch (type)
+	{
+		case TAG_CLASS:      result = JK_CLASS;         break;
+		case TAG_ENUM:       result = JK_ENUM;          break;
+		case TAG_ENUMERATOR: result = JK_ENUM_CONSTANT; break;
+		case TAG_FIELD:      result = JK_FIELD;         break;
+		case TAG_INTERFACE:  result = JK_INTERFACE;     break;
+		case TAG_LOCAL:      result = JK_LOCAL;         break;
+		case TAG_METHOD:     result = JK_METHOD;        break;
+		case TAG_PACKAGE:    result = JK_PACKAGE;       break;
+
+		default: Assert ("Bad Java tag type" == NULL); break;
+	}
+	return result;
+}
+
+static veraKind veraTagKind (const tagType type) {
+	veraKind result = VK_UNDEFINED;
+	switch (type)
+	{
+		case TAG_CLASS:      result = VK_CLASS;           break;
+		case TAG_ENUM:       result = VK_ENUMERATION;     break;
+		case TAG_ENUMERATOR: result = VK_ENUMERATOR;      break;
+		case TAG_FUNCTION:   result = VK_FUNCTION;        break;
+		case TAG_LOCAL:      result = VK_LOCAL;           break;
+		case TAG_MEMBER:     result = VK_MEMBER;          break;
+		case TAG_PROGRAM:    result = VK_PROGRAM;         break;
+		case TAG_PROTOTYPE:  result = VK_PROTOTYPE;       break;
+		case TAG_TASK:       result = VK_TASK;            break;
+		case TAG_TYPEDEF:    result = VK_TYPEDEF;         break;
+		case TAG_VARIABLE:   result = VK_VARIABLE;        break;
+		case TAG_EXTERN_VAR: result = VK_EXTERN_VARIABLE; break;
+
+		default: Assert ("Bad Vera tag type" == NULL); break;
+	}
+	return result;
+}
+
+static const char *tagName (const tagType type)
+{
+	const char* result;
+	if (isLanguage (Lang_csharp))
+		result = CsharpKinds [csharpTagKind (type)].name;
+	else if (isLanguage (Lang_java))
+		result = JavaKinds [javaTagKind (type)].name;
+	else if (isLanguage (Lang_vera))
+		result = VeraKinds [veraTagKind (type)].name;
+	else
+		result = CKinds [cTagKind (type)].name;
+	return result;
+}
+
+static int tagLetter (const tagType type)
+{
+	int result;
+	if (isLanguage (Lang_csharp))
+		result = CsharpKinds [csharpTagKind (type)].letter;
+	else if (isLanguage (Lang_java))
+		result = JavaKinds [javaTagKind (type)].letter;
+	else if (isLanguage (Lang_vera))
+		result = VeraKinds [veraTagKind (type)].letter;
+	else
+		result = CKinds [cTagKind (type)].letter;
+	return result;
+}
+
+static boolean includeTag (const tagType type, const boolean isFileScope)
+{
+	boolean result;
+	if (isFileScope  &&  ! Option.include.fileScope)
+		result = FALSE;
+	else if (isLanguage (Lang_csharp))
+		result = CsharpKinds [csharpTagKind (type)].enabled;
+	else if (isLanguage (Lang_java))
+		result = JavaKinds [javaTagKind (type)].enabled;
+	else if (isLanguage (Lang_vera))
+		result = VeraKinds [veraTagKind (type)].enabled;
+	else
+		result = CKinds [cTagKind (type)].enabled;
+	return result;
+}
+
+static tagType declToTagType (const declType declaration)
+{
+	tagType type = TAG_UNDEFINED;
+
+	switch (declaration)
+	{
+		case DECL_CLASS:        type = TAG_CLASS;       break;
+		case DECL_ENUM:         type = TAG_ENUM;        break;
+		case DECL_EVENT:        type = TAG_EVENT;       break;
+		case DECL_FUNCTION:     type = TAG_FUNCTION;    break;
+		case DECL_INTERFACE:    type = TAG_INTERFACE;   break;
+		case DECL_NAMESPACE:    type = TAG_NAMESPACE;   break;
+		case DECL_PROGRAM:      type = TAG_PROGRAM;     break;
+		case DECL_TASK:         type = TAG_TASK;        break;
+		case DECL_STRUCT:       type = TAG_STRUCT;      break;
+		case DECL_UNION:        type = TAG_UNION;       break;
+
+		default: Assert ("Unexpected declaration" == NULL); break;
+	}
+	return type;
+}
+
+static const char* accessField (const statementInfo *const st)
+{
+	const char* result = NULL;
+	if (isLanguage (Lang_cpp)  &&  st->scope == SCOPE_FRIEND)
+		result = "friend";
+	else if (st->member.access != ACCESS_UNDEFINED)
+		result = accessString (st->member.access);
+	return result;
+}
+
+static void addContextSeparator (vString *const scope)
+{
+	if (isLanguage (Lang_c)  ||  isLanguage (Lang_cpp))
+		vStringCatS (scope, "::");
+	else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
+		vStringCatS (scope, ".");
+}
+
+static void addOtherFields (tagEntryInfo* const tag, const tagType type,
+							const statementInfo *const st,
+							vString *const scope, vString *const typeRef)
+{
+	/*  For selected tag types, append an extension flag designating the
+	 *  parent object in which the tag is defined.
+	 */
+	switch (type)
+	{
+		default: break;
+
+		case TAG_FUNCTION:
+		case TAG_METHOD:
+		case TAG_PROTOTYPE:
+			if (vStringLength (Signature) > 0)
+				tag->extensionFields.signature = vStringValue (Signature);
+		case TAG_CLASS:
+		case TAG_ENUM:
+		case TAG_ENUMERATOR:
+		case TAG_EVENT:
+		case TAG_FIELD:
+		case TAG_INTERFACE:
+		case TAG_MEMBER:
+		case TAG_NAMESPACE:
+		case TAG_PROPERTY:
+		case TAG_STRUCT:
+		case TAG_TASK:
+		case TAG_TYPEDEF:
+		case TAG_UNION:
+			if (vStringLength (scope) > 0  &&
+				(isMember (st) || st->parent->declaration == DECL_NAMESPACE))
+			{
+				if (isType (st->context, TOKEN_NAME))
+					tag->extensionFields.scope [0] = tagName (TAG_CLASS);
+				else
+					tag->extensionFields.scope [0] =
+						tagName (declToTagType (parentDecl (st)));
+				tag->extensionFields.scope [1] = vStringValue (scope);
+			}
+			if ((type == TAG_CLASS  ||  type == TAG_INTERFACE  ||
+				 type == TAG_STRUCT) && vStringLength (st->parentClasses) > 0)
+			{
+
+				tag->extensionFields.inheritance =
+						vStringValue (st->parentClasses);
+			}
+			if (st->implementation != IMP_DEFAULT &&
+				(isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
+				 isLanguage (Lang_java)))
+			{
+				tag->extensionFields.implementation =
+						implementationString (st->implementation);
+			}
+			if (isMember (st))
+			{
+				tag->extensionFields.access = accessField (st);
+			}
+			break;
+	}
+
+	/* Add typename info, type of the tag and name of struct/union/etc. */
+	if ((type == TAG_TYPEDEF || type == TAG_VARIABLE || type == TAG_MEMBER)
+			&& isContextualStatement(st))
+	{
+		char *p;
+
+		tag->extensionFields.typeRef [0] =
+						tagName (declToTagType (st->declaration));
+		p = vStringValue (st->blockName->name);
+
+		/*  If there was no {} block get the name from the token before the
+		 *  name (current token is ';' or ',', previous token is the name).
+		 */
+		if (p == NULL || *p == '\0')
+		{
+			tokenInfo *const prev2 = prevToken (st, 2);
+			if (isType (prev2, TOKEN_NAME))
+				p = vStringValue (prev2->name);
+		}
+
+		/* Prepend the scope name if there is one. */
+		if (vStringLength (scope) > 0)
+		{
+			vStringCopy(typeRef, scope);
+			addContextSeparator (typeRef);
+			vStringCatS(typeRef, p);
+			p = vStringValue (typeRef);
+		}
+		tag->extensionFields.typeRef [1] = p;
+	}
+}
+
+static void findScopeHierarchy (vString *const string,
+								const statementInfo *const st)
+{
+	vStringClear (string);
+	if (isType (st->context, TOKEN_NAME))
+		vStringCopy (string, st->context->name);
+	if (st->parent != NULL)
+	{
+		vString *temp = vStringNew ();
+		const statementInfo *s;
+		for (s = st->parent  ;  s != NULL  ;  s = s->parent)
+		{
+			if (isContextualStatement (s) ||
+				s->declaration == DECL_NAMESPACE ||
+				s->declaration == DECL_PROGRAM)
+			{
+				vStringCopy (temp, string);
+				vStringClear (string);
+				Assert (isType (s->blockName, TOKEN_NAME));
+				if (isType (s->context, TOKEN_NAME) &&
+					vStringLength (s->context->name) > 0)
+				{
+					vStringCat (string, s->context->name);
+					addContextSeparator (string);
+				}
+				vStringCat (string, s->blockName->name);
+				if (vStringLength (temp) > 0)
+					addContextSeparator (string);
+				vStringCat (string, temp);
+			}
+		}
+		vStringDelete (temp);
+	}
+}
+
+static void makeExtraTagEntry (const tagType type, tagEntryInfo *const e,
+							   vString *const scope)
+{
+	if (Option.include.qualifiedTags  &&
+		scope != NULL  &&  vStringLength (scope) > 0)
+	{
+		vString *const scopedName = vStringNew ();
+
+		if (type != TAG_ENUMERATOR)
+			vStringCopy (scopedName, scope);
+		else
+		{
+			/* remove last component (i.e. enumeration name) from scope */
+			const char* const sc = vStringValue (scope);
+			const char* colon = strrchr (sc, ':');
+			if (colon != NULL)
+			{
+				while (*colon == ':'  &&  colon > sc)
+					--colon;
+				vStringNCopy (scopedName, scope, colon + 1 - sc);
+			}
+		}
+		if (vStringLength (scopedName) > 0)
+		{
+			addContextSeparator (scopedName);
+			vStringCatS (scopedName, e->name);
+			e->name = vStringValue (scopedName);
+			makeTagEntry (e);
+		}
+		vStringDelete (scopedName);
+	}
+}
+
+static void makeTag (const tokenInfo *const token,
+					 const statementInfo *const st,
+					 boolean isFileScope, const tagType type)
+{
+	/*  Nothing is really of file scope when it appears in a header file.
+	 */
+	isFileScope = (boolean) (isFileScope && ! isHeaderFile ());
+
+	if (isType (token, TOKEN_NAME)  &&  vStringLength (token->name) > 0  &&
+		includeTag (type, isFileScope))
+	{
+		vString *scope = vStringNew ();
+		/* Use "typeRef" to store the typename from addOtherFields() until
+		 * it's used in makeTagEntry().
+		 */
+		vString *typeRef = vStringNew ();
+		tagEntryInfo e;
+
+		initTagEntry (&e, vStringValue (token->name));
+
+		e.lineNumber	= token->lineNumber;
+		e.filePosition	= token->filePosition;
+		e.isFileScope	= isFileScope;
+		e.kindName		= tagName (type);
+		e.kind			= tagLetter (type);
+
+		findScopeHierarchy (scope, st);
+		addOtherFields (&e, type, st, scope, typeRef);
+
+		makeTagEntry (&e);
+		makeExtraTagEntry (type, &e, scope);
+		vStringDelete (scope);
+		vStringDelete (typeRef);
+	}
+}
+
+static boolean isValidTypeSpecifier (const declType declaration)
+{
+	boolean result;
+	switch (declaration)
+	{
+		case DECL_BASE:
+		case DECL_CLASS:
+		case DECL_ENUM:
+		case DECL_EVENT:
+		case DECL_STRUCT:
+		case DECL_UNION:
+			result = TRUE;
+			break;
+
+		default:
+			result = FALSE;
+			break;
+	}
+	return result;
+}
+
+static void qualifyEnumeratorTag (const statementInfo *const st,
+								  const tokenInfo *const nameToken)
+{
+	if (isType (nameToken, TOKEN_NAME))
+		makeTag (nameToken, st, TRUE, TAG_ENUMERATOR);
+}
+
+static void qualifyFunctionTag (const statementInfo *const st,
+								const tokenInfo *const nameToken)
+{
+	if (isType (nameToken, TOKEN_NAME))
+	{
+		tagType type;
+		const boolean isFileScope =
+						(boolean) (st->member.access == ACCESS_PRIVATE ||
+						(!isMember (st)  &&  st->scope == SCOPE_STATIC));
+		if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
+			type = TAG_METHOD;
+		else if (isLanguage (Lang_vera)  &&  st->declaration == DECL_TASK)
+			type = TAG_TASK;
+		else
+			type = TAG_FUNCTION;
+		makeTag (nameToken, st, isFileScope, type);
+	}
+}
+
+static void qualifyFunctionDeclTag (const statementInfo *const st,
+									const tokenInfo *const nameToken)
+{
+	if (! isType (nameToken, TOKEN_NAME))
+		;
+	else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
+		qualifyFunctionTag (st, nameToken);
+	else if (st->scope == SCOPE_TYPEDEF)
+		makeTag (nameToken, st, TRUE, TAG_TYPEDEF);
+	else if (isValidTypeSpecifier (st->declaration) && ! isLanguage (Lang_csharp))
+		makeTag (nameToken, st, TRUE, TAG_PROTOTYPE);
+}
+
+static void qualifyCompoundTag (const statementInfo *const st,
+								const tokenInfo *const nameToken)
+{
+	if (isType (nameToken, TOKEN_NAME))
+	{
+		const tagType type = declToTagType (st->declaration);
+		const boolean fileScoped = (boolean)
+				(!(isLanguage (Lang_java) ||
+				   isLanguage (Lang_csharp) ||
+				   isLanguage (Lang_vera)));
+
+		if (type != TAG_UNDEFINED)
+			makeTag (nameToken, st, fileScoped, type);
+	}
+}
+
+static void qualifyBlockTag (statementInfo *const st,
+							 const tokenInfo *const nameToken)
+{
+	switch (st->declaration)
+	{
+		case DECL_CLASS:
+		case DECL_ENUM:
+		case DECL_INTERFACE:
+		case DECL_NAMESPACE:
+		case DECL_PROGRAM:
+		case DECL_STRUCT:
+		case DECL_UNION:
+			qualifyCompoundTag (st, nameToken);
+			break;
+		default: break;
+	}
+}
+
+static void qualifyVariableTag (const statementInfo *const st,
+								const tokenInfo *const nameToken)
+{
+	/*	We have to watch that we do not interpret a declaration of the
+	 *	form "struct tag;" as a variable definition. In such a case, the
+	 *	token preceding the name will be a keyword.
+	 */
+	if (! isType (nameToken, TOKEN_NAME))
+		;
+	else if (st->scope == SCOPE_TYPEDEF)
+		makeTag (nameToken, st, TRUE, TAG_TYPEDEF);
+	else if (st->declaration == DECL_EVENT)
+		makeTag (nameToken, st, (boolean) (st->member.access == ACCESS_PRIVATE),
+				TAG_EVENT);
+	else if (st->declaration == DECL_PACKAGE)
+		makeTag (nameToken, st, FALSE, TAG_PACKAGE);
+	else if (isValidTypeSpecifier (st->declaration))
+	{
+		if (st->notVariable)
+			;
+		else if (isMember (st))
+		{
+			if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
+				makeTag (nameToken, st,
+						(boolean) (st->member.access == ACCESS_PRIVATE), TAG_FIELD);
+			else if (st->scope == SCOPE_GLOBAL  ||  st->scope == SCOPE_STATIC)
+				makeTag (nameToken, st, TRUE, TAG_MEMBER);
+		}
+		else
+		{
+			if (st->scope == SCOPE_EXTERN  ||  ! st->haveQualifyingName)
+				makeTag (nameToken, st, FALSE, TAG_EXTERN_VAR);
+			else if (st->inFunction)
+				makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
+						TAG_LOCAL);
+			else
+				makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
+						TAG_VARIABLE);
+		}
+	}
+}
+
+/*
+*   Parsing functions
+*/
+
+static int skipToOneOf (const char *const chars)
+{
+	int c;
+	do
+		c = cppGetc ();
+	while (c != EOF  &&  c != '\0'  &&  strchr (chars, c) == NULL);
+	return c;
+}
+
+/*  Skip to the next non-white character.
+ */
+static int skipToNonWhite (void)
+{
+	boolean found = FALSE;
+	int c;
+
+#if 0
+	do
+		c = cppGetc ();
+	while (isspace (c));
+#else
+	while (1)
+	{
+		c = cppGetc ();
+		if (isspace (c))
+			found = TRUE;
+		else
+			break;
+	}
+	if (CollectingSignature && found)
+		vStringPut (Signature, ' ');
+#endif
+
+	return c;
+}
+
+/*  Skips to the next brace in column 1. This is intended for cases where
+ *  preprocessor constructs result in unbalanced braces.
+ */
+static void skipToFormattedBraceMatch (void)
+{
+	int c, next;
+
+	c = cppGetc ();
+	next = cppGetc ();
+	while (c != EOF  &&  (c != '\n'  ||  next != '}'))
+	{
+		c = next;
+		next = cppGetc ();
+	}
+}
+
+/*  Skip to the matching character indicated by the pair string. If skipping
+ *  to a matching brace and any brace is found within a different level of a
+ *  #if conditional statement while brace formatting is in effect, we skip to
+ *  the brace matched by its formatting. It is assumed that we have already
+ *  read the character which starts the group (i.e. the first character of
+ *  "pair").
+ */
+static void skipToMatch (const char *const pair)
+{
+	const boolean braceMatching = (boolean) (strcmp ("{}", pair) == 0);
+	const boolean braceFormatting = (boolean) (isBraceFormat () && braceMatching);
+	const unsigned int initialLevel = getDirectiveNestLevel ();
+	const int begin = pair [0], end = pair [1];
+	const unsigned long inputLineNumber = getInputLineNumber ();
+	int matchLevel = 1;
+	int c = '\0';
+
+	while (matchLevel > 0  &&  (c = skipToNonWhite ()) != EOF)
+	{
+		if (CollectingSignature)
+			vStringPut (Signature, c);
+		if (c == begin)
+		{
+			++matchLevel;
+			if (braceFormatting  &&  getDirectiveNestLevel () != initialLevel)
+			{
+				skipToFormattedBraceMatch ();
+				break;
+			}
+		}
+		else if (c == end)
+		{
+			--matchLevel;
+			if (braceFormatting  &&  getDirectiveNestLevel () != initialLevel)
+			{
+				skipToFormattedBraceMatch ();
+				break;
+			}
+		}
+	}
+	if (c == EOF)
+	{
+		verbose ("%s: failed to find match for '%c' at line %lu\n",
+				getInputFileName (), begin, inputLineNumber);
+		if (braceMatching)
+			longjmp (Exception, (int) ExceptionBraceFormattingError);
+		else
+			longjmp (Exception, (int) ExceptionFormattingError);
+	}
+}
+
+static void skipParens (void)
+{
+	const int c = skipToNonWhite ();
+
+	if (c == '(')
+		skipToMatch ("()");
+	else
+		cppUngetc (c);
+}
+
+static void skipBraces (void)
+{
+	const int c = skipToNonWhite ();
+
+	if (c == '{')
+		skipToMatch ("{}");
+	else
+		cppUngetc (c);
+}
+
+static keywordId analyzeKeyword (const char *const name)
+{
+	const keywordId id = (keywordId) lookupKeyword (name, getSourceLanguage ());
+	return id;
+}
+
+static void analyzeIdentifier (tokenInfo *const token)
+{
+	char *const name = vStringValue (token->name);
+	const char *replacement = NULL;
+	boolean parensToo = FALSE;
+
+	if (isLanguage (Lang_java)  ||
+		! isIgnoreToken (name, &parensToo, &replacement))
+	{
+		if (replacement != NULL)
+			token->keyword = analyzeKeyword (replacement);
+		else
+			token->keyword = analyzeKeyword (vStringValue (token->name));
+
+		if (token->keyword == KEYWORD_NONE)
+			token->type = TOKEN_NAME;
+		else
+			token->type = TOKEN_KEYWORD;
+	}
+	else
+	{
+		initToken (token);
+		if (parensToo)
+		{
+			int c = skipToNonWhite ();
+
+			if (c == '(')
+				skipToMatch ("()");
+		}
+	}
+}
+
+static void readIdentifier (tokenInfo *const token, const int firstChar)
+{
+	vString *const name = token->name;
+	int c = firstChar;
+	boolean first = TRUE;
+
+	initToken (token);
+
+	/* Bug #1585745: strangely, C++ destructors allow whitespace between
+	 * the ~ and the class name. */
+	if (isLanguage (Lang_cpp) && firstChar == '~')
+	{
+		vStringPut (name, c);
+		c = skipToNonWhite ();
+	}
+
+	do
+	{
+		vStringPut (name, c);
+		if (CollectingSignature)
+		{
+			if (!first)
+				vStringPut (Signature, c);
+			first = FALSE;
+		}
+		c = cppGetc ();
+	} while (isident (c) || ((isLanguage (Lang_java) || isLanguage (Lang_csharp)) && (isHighChar (c) || c == '.')));
+	vStringTerminate (name);
+	cppUngetc (c);        /* unget non-identifier character */
+
+	analyzeIdentifier (token);
+}
+
+static void readPackageName (tokenInfo *const token, const int firstChar)
+{
+	vString *const name = token->name;
+	int c = firstChar;
+
+	initToken (token);
+
+	while (isident (c)  ||  c == '.')
+	{
+		vStringPut (name, c);
+		c = cppGetc ();
+	}
+	vStringTerminate (name);
+	cppUngetc (c);        /* unget non-package character */
+}
+
+static void readPackageOrNamespace (statementInfo *const st, const declType declaration)
+{
+	st->declaration = declaration;
+	
+	if (declaration == DECL_NAMESPACE && !isLanguage (Lang_csharp))
+	{
+		/* In C++ a namespace is specified one level at a time. */
+		return;
+	}
+	else
+	{
+		/* In C#, a namespace can also be specified like a Java package name. */
+		tokenInfo *const token = activeToken (st);
+		Assert (isType (token, TOKEN_KEYWORD));
+		readPackageName (token, skipToNonWhite ());
+		token->type = TOKEN_NAME;
+		st->gotName = TRUE;
+		st->haveQualifyingName = TRUE;
+	}
+}
+
+static void processName (statementInfo *const st)
+{
+	Assert (isType (activeToken (st), TOKEN_NAME));
+	if (st->gotName  &&  st->declaration == DECL_NONE)
+		st->declaration = DECL_BASE;
+	st->gotName = TRUE;
+	st->haveQualifyingName = TRUE;
+}
+
+static void readOperator (statementInfo *const st)
+{
+	const char *const acceptable = "+-*/%^&|~!=<>,[]";
+	const tokenInfo* const prev = prevToken (st,1);
+	tokenInfo *const token = activeToken (st);
+	vString *const name = token->name;
+	int c = skipToNonWhite ();
+
+	/*  When we arrive here, we have the keyword "operator" in 'name'.
+	 */
+	if (isType (prev, TOKEN_KEYWORD) && (prev->keyword == KEYWORD_ENUM ||
+		 prev->keyword == KEYWORD_STRUCT || prev->keyword == KEYWORD_UNION))
+		;        /* ignore "operator" keyword if preceded by these keywords */
+	else if (c == '(')
+	{
+		/*  Verify whether this is a valid function call (i.e. "()") operator.
+		 */
+		if (cppGetc () == ')')
+		{
+			vStringPut (name, ' ');  /* always separate operator from keyword */
+			c = skipToNonWhite ();
+			if (c == '(')
+				vStringCatS (name, "()");
+		}
+		else
+		{
+			skipToMatch ("()");
+			c = cppGetc ();
+		}
+	}
+	else if (isident1 (c))
+	{
+		/*  Handle "new" and "delete" operators, and conversion functions
+		 *  (per 13.3.1.1.2 [2] of the C++ spec).
+		 */
+		boolean whiteSpace = TRUE;  /* default causes insertion of space */
+		do
+		{
+			if (isspace (c))
+				whiteSpace = TRUE;
+			else
+			{
+				if (whiteSpace)
+				{
+					vStringPut (name, ' ');
+					whiteSpace = FALSE;
+				}
+				vStringPut (name, c);
+			}
+			c = cppGetc ();
+		} while (! isOneOf (c, "(;")  &&  c != EOF);
+		vStringTerminate (name);
+	}
+	else if (isOneOf (c, acceptable))
+	{
+		vStringPut (name, ' ');  /* always separate operator from keyword */
+		do
+		{
+			vStringPut (name, c);
+			c = cppGetc ();
+		} while (isOneOf (c, acceptable));
+		vStringTerminate (name);
+	}
+
+	cppUngetc (c);
+
+	token->type	= TOKEN_NAME;
+	token->keyword = KEYWORD_NONE;
+	processName (st);
+}
+
+static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
+{
+	dest->type         = src->type;
+	dest->keyword      = src->keyword;
+	dest->filePosition = src->filePosition;
+	dest->lineNumber   = src->lineNumber;
+	vStringCopy (dest->name, src->name);
+}
+
+static void setAccess (statementInfo *const st, const accessType access)
+{
+	if (isMember (st))
+	{
+		if (isLanguage (Lang_cpp))
+		{
+			int c = skipToNonWhite ();
+
+			if (c == ':')
+				reinitStatement (st, FALSE);
+			else
+				cppUngetc (c);
+
+			st->member.accessDefault = access;
+		}
+		st->member.access = access;
+	}
+}
+
+static void discardTypeList (tokenInfo *const token)
+{
+	int c = skipToNonWhite ();
+	while (isident1 (c))
+	{
+		readIdentifier (token, c);
+		c = skipToNonWhite ();
+		if (c == '.'  ||  c == ',')
+			c = skipToNonWhite ();
+	}
+	cppUngetc (c);
+}
+
+static void addParentClass (statementInfo *const st, tokenInfo *const token)
+{
+	if (vStringLength (token->name) > 0  &&
+		vStringLength (st->parentClasses) > 0)
+	{
+		vStringPut (st->parentClasses, ',');
+	}
+	vStringCat (st->parentClasses, token->name);
+}
+
+static void readParents (statementInfo *const st, const int qualifier)
+{
+	tokenInfo *const token = newToken ();
+	tokenInfo *const parent = newToken ();
+	int c;
+
+	do
+	{
+		c = skipToNonWhite ();
+		if (isident1 (c))
+		{
+			readIdentifier (token, c);
+			if (isType (token, TOKEN_NAME))
+				vStringCat (parent->name, token->name);
+			else
+			{
+				addParentClass (st, parent);
+				initToken (parent);
+			}
+		}
+		else if (c == qualifier)
+			vStringPut (parent->name, c);
+		else if (c == '<')
+			skipToMatch ("<>");
+		else if (isType (token, TOKEN_NAME))
+		{
+			addParentClass (st, parent);
+			initToken (parent);
+		}
+	} while (c != '{'  &&  c != EOF);
+	cppUngetc (c);
+	deleteToken (parent);
+	deleteToken (token);
+}
+
+static void skipStatement (statementInfo *const st)
+{
+	st->declaration = DECL_IGNORE;
+	skipToOneOf (";");
+}
+
+static void processInterface (statementInfo *const st)
+{
+	st->declaration = DECL_INTERFACE;
+}
+
+static void processToken (tokenInfo *const token, statementInfo *const st)
+{
+	switch (token->keyword)        /* is it a reserved word? */
+	{
+		default: break;
+
+		case KEYWORD_NONE:      processName (st);                       break;
+		case KEYWORD_ABSTRACT:  st->implementation = IMP_ABSTRACT;      break;
+		case KEYWORD_ATTRIBUTE: skipParens (); initToken (token);       break;
+		case KEYWORD_BIND:      st->declaration = DECL_BASE;            break;
+		case KEYWORD_BIT:       st->declaration = DECL_BASE;            break;
+		case KEYWORD_CATCH:     skipParens (); skipBraces ();           break;
+		case KEYWORD_CHAR:      st->declaration = DECL_BASE;            break;
+		case KEYWORD_CLASS:     st->declaration = DECL_CLASS;           break;
+		case KEYWORD_CONST:     st->declaration = DECL_BASE;            break;
+		case KEYWORD_DOUBLE:    st->declaration = DECL_BASE;            break;
+		case KEYWORD_ENUM:      st->declaration = DECL_ENUM;            break;
+		case KEYWORD_EXTENDS:   readParents (st, '.');
+		                        setToken (st, TOKEN_NONE);              break;
+		case KEYWORD_FLOAT:     st->declaration = DECL_BASE;            break;
+		case KEYWORD_FUNCTION:  st->declaration = DECL_BASE;            break;
+		case KEYWORD_FRIEND:    st->scope       = SCOPE_FRIEND;         break;
+		case KEYWORD_GOTO:      skipStatement (st);                     break;
+		case KEYWORD_IMPLEMENTS:readParents (st, '.');
+		                        setToken (st, TOKEN_NONE);              break;
+		case KEYWORD_IMPORT:    skipStatement (st);                     break;
+		case KEYWORD_INT:       st->declaration = DECL_BASE;            break;
+		case KEYWORD_INTEGER:   st->declaration = DECL_BASE;            break;
+		case KEYWORD_INTERFACE: processInterface (st);                  break;
+		case KEYWORD_LOCAL:     setAccess (st, ACCESS_LOCAL);           break;
+		case KEYWORD_LONG:      st->declaration = DECL_BASE;            break;
+		case KEYWORD_OPERATOR:  readOperator (st);                      break;
+		case KEYWORD_PRIVATE:   setAccess (st, ACCESS_PRIVATE);         break;
+		case KEYWORD_PROGRAM:   st->declaration = DECL_PROGRAM;         break;
+		case KEYWORD_PROTECTED: setAccess (st, ACCESS_PROTECTED);       break;
+		case KEYWORD_PUBLIC:    setAccess (st, ACCESS_PUBLIC);          break;
+		case KEYWORD_RETURN:    skipStatement (st);                     break;
+		case KEYWORD_SHORT:     st->declaration = DECL_BASE;            break;
+		case KEYWORD_SIGNED:    st->declaration = DECL_BASE;            break;
+		case KEYWORD_STRING:    st->declaration = DECL_BASE;            break;
+		case KEYWORD_STRUCT:    st->declaration = DECL_STRUCT;          break;
+		case KEYWORD_TASK:      st->declaration = DECL_TASK;            break;
+		case KEYWORD_THROWS:    discardTypeList (token);                break;
+		case KEYWORD_UNION:     st->declaration = DECL_UNION;           break;
+		case KEYWORD_UNSIGNED:  st->declaration = DECL_BASE;            break;
+		case KEYWORD_USING:     skipStatement (st);                     break;
+		case KEYWORD_VOID:      st->declaration = DECL_BASE;            break;
+		case KEYWORD_VOLATILE:  st->declaration = DECL_BASE;            break;
+		case KEYWORD_VIRTUAL:   st->implementation = IMP_VIRTUAL;       break;
+		case KEYWORD_WCHAR_T:   st->declaration = DECL_BASE;            break;
+		
+		case KEYWORD_NAMESPACE: readPackageOrNamespace (st, DECL_NAMESPACE); break;
+		case KEYWORD_PACKAGE:   readPackageOrNamespace (st, DECL_PACKAGE);   break;
+		
+		case KEYWORD_EVENT:
+			if (isLanguage (Lang_csharp))
+				st->declaration = DECL_EVENT;
+			break;
+
+		case KEYWORD_TYPEDEF:
+			reinitStatement (st, FALSE);
+			st->scope = SCOPE_TYPEDEF;
+			break;
+
+		case KEYWORD_EXTERN:
+			if (! isLanguage (Lang_csharp) || !st->gotName)
+			{
+				reinitStatement (st, FALSE);
+				st->scope = SCOPE_EXTERN;
+				st->declaration = DECL_BASE;
+			}
+			break;
+
+		case KEYWORD_STATIC:
+			if (! (isLanguage (Lang_java) || isLanguage (Lang_csharp)))
+			{
+				reinitStatement (st, FALSE);
+				st->scope = SCOPE_STATIC;
+				st->declaration = DECL_BASE;
+			}
+			break;
+
+		case KEYWORD_FOR:
+		case KEYWORD_FOREACH:
+		case KEYWORD_IF:
+		case KEYWORD_SWITCH:
+		case KEYWORD_WHILE:
+		{
+			int c = skipToNonWhite ();
+			if (c == '(')
+				skipToMatch ("()");
+			break;
+		}
+	}
+}
+
+/*
+*   Parenthesis handling functions
+*/
+
+static void restartStatement (statementInfo *const st)
+{
+	tokenInfo *const save = newToken ();
+	tokenInfo *token = activeToken (st);
+
+	copyToken (save, token);
+	DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>");)
+	reinitStatement (st, FALSE);
+	token = activeToken (st);
+	copyToken (token, save);
+	deleteToken (save);
+	processToken (token, st);
+}
+
+/*  Skips over a the mem-initializer-list of a ctor-initializer, defined as:
+ *
+ *  mem-initializer-list:
+ *    mem-initializer, mem-initializer-list
+ *
+ *  mem-initializer:
+ *    [::] [nested-name-spec] class-name (...)
+ *    identifier
+ */
+static void skipMemIntializerList (tokenInfo *const token)
+{
+	int c;
+
+	do
+	{
+		c = skipToNonWhite ();
+		while (isident1 (c)  ||  c == ':')
+		{
+			if (c != ':')
+				readIdentifier (token, c);
+			c = skipToNonWhite ();
+		}
+		if (c == '<')
+		{
+			skipToMatch ("<>");
+			c = skipToNonWhite ();
+		}
+		if (c == '(')
+		{
+			skipToMatch ("()");
+			c = skipToNonWhite ();
+		}
+	} while (c == ',');
+	cppUngetc (c);
+}
+
+static void skipMacro (statementInfo *const st)
+{
+	tokenInfo *const prev2 = prevToken (st, 2);
+
+	if (isType (prev2, TOKEN_NAME))
+		retardToken (st);
+	skipToMatch ("()");
+}
+
+/*  Skips over characters following the parameter list. This will be either
+ *  non-ANSI style function declarations or C++ stuff. Our choices:
+ *
+ *  C (K&R):
+ *    int func ();
+ *    int func (one, two) int one; float two; {...}
+ *  C (ANSI):
+ *    int func (int one, float two);
+ *    int func (int one, float two) {...}
+ *  C++:
+ *    int foo (...) [const|volatile] [throw (...)];
+ *    int foo (...) [const|volatile] [throw (...)] [ctor-initializer] {...}
+ *    int foo (...) [const|volatile] [throw (...)] try [ctor-initializer] {...}
+ *        catch (...) {...}
+ */
+static boolean skipPostArgumentStuff (
+		statementInfo *const st, parenInfo *const info)
+{
+	tokenInfo *const token = activeToken (st);
+	unsigned int parameters = info->parameterCount;
+	unsigned int elementCount = 0;
+	boolean restart = FALSE;
+	boolean end = FALSE;
+	int c = skipToNonWhite ();
+
+	do
+	{
+		switch (c)
+		{
+		case ')':                               break;
+		case ':': skipMemIntializerList (token);break;  /* ctor-initializer */
+		case '[': skipToMatch ("[]");           break;
+		case '=': cppUngetc (c); end = TRUE;    break;
+		case '{': cppUngetc (c); end = TRUE;    break;
+		case '}': cppUngetc (c); end = TRUE;    break;
+
+		case '(':
+			if (elementCount > 0)
+				++elementCount;
+			skipToMatch ("()");
+			break;
+
+		case ';':
+			if (parameters == 0  ||  elementCount < 2)
+			{
+				cppUngetc (c);
+				end = TRUE;
+			}
+			else if (--parameters == 0)
+				end = TRUE;
+			break;
+
+		default:
+			if (isident1 (c))
+			{
+				readIdentifier (token, c);
+				switch (token->keyword)
+				{
+				case KEYWORD_ATTRIBUTE: skipParens ();  break;
+				case KEYWORD_THROW:     skipParens ();  break;
+				case KEYWORD_TRY:                       break;
+
+				case KEYWORD_CONST:
+				case KEYWORD_VOLATILE:
+					if (vStringLength (Signature) > 0)
+					{
+						vStringPut (Signature, ' ');
+						vStringCat (Signature, token->name);
+					}
+					break;
+
+				case KEYWORD_CATCH:
+				case KEYWORD_CLASS:
+				case KEYWORD_EXPLICIT:
+				case KEYWORD_EXTERN:
+				case KEYWORD_FRIEND:
+				case KEYWORD_INLINE:
+				case KEYWORD_MUTABLE:
+				case KEYWORD_NAMESPACE:
+				case KEYWORD_NEW:
+				case KEYWORD_NEWCOV:
+				case KEYWORD_OPERATOR:
+				case KEYWORD_OVERLOAD:
+				case KEYWORD_PRIVATE:
+				case KEYWORD_PROTECTED:
+				case KEYWORD_PUBLIC:
+				case KEYWORD_STATIC:
+				case KEYWORD_TEMPLATE:
+				case KEYWORD_TYPEDEF:
+				case KEYWORD_TYPENAME:
+				case KEYWORD_USING:
+				case KEYWORD_VIRTUAL:
+					/* Never allowed within parameter declarations. */
+					restart = TRUE;
+					end = TRUE;
+					break;
+
+				default:
+					if (isType (token, TOKEN_NONE))
+						;
+					else if (info->isKnrParamList  &&  info->parameterCount > 0)
+						++elementCount;
+					else
+					{
+						/*  If we encounter any other identifier immediately
+						 *  following an empty parameter list, this is almost
+						 *  certainly one of those Microsoft macro "thingies"
+						 *  that the automatic source code generation sticks
+						 *  in. Terminate the current statement.
+						 */
+						restart = TRUE;
+						end = TRUE;
+					}
+					break;
+				}
+			}
+		}
+		if (! end)
+		{
+			c = skipToNonWhite ();
+			if (c == EOF)
+				end = TRUE;
+		}
+	} while (! end);
+
+	if (restart)
+		restartStatement (st);
+	else
+		setToken (st, TOKEN_NONE);
+
+	return (boolean) (c != EOF);
+}
+
+static void skipJavaThrows (statementInfo *const st)
+{
+	tokenInfo *const token = activeToken (st);
+	int c = skipToNonWhite ();
+
+	if (isident1 (c))
+	{
+		readIdentifier (token, c);
+		if (token->keyword == KEYWORD_THROWS)
+		{
+			do
+			{
+				c = skipToNonWhite ();
+				if (isident1 (c))
+				{
+					readIdentifier (token, c);
+					c = skipToNonWhite ();
+				}
+			} while (c == '.'  ||  c == ',');
+		}
+	}
+	cppUngetc (c);
+	setToken (st, TOKEN_NONE);
+}
+
+static void analyzePostParens (statementInfo *const st, parenInfo *const info)
+{
+	const unsigned long inputLineNumber = getInputLineNumber ();
+	int c = skipToNonWhite ();
+
+	cppUngetc (c);
+	if (isOneOf (c, "{;,="))
+		;
+	else if (isLanguage (Lang_java))
+		skipJavaThrows (st);
+	else
+	{
+		if (! skipPostArgumentStuff (st, info))
+		{
+			verbose (
+				"%s: confusing argument declarations beginning at line %lu\n",
+				getInputFileName (), inputLineNumber);
+			longjmp (Exception, (int) ExceptionFormattingError);
+		}
+	}
+}
+
+static boolean languageSupportsGenerics (void)
+{
+	return (boolean) (isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
+		isLanguage (Lang_java));
+}
+
+static void processAngleBracket (void)
+{
+	int c = cppGetc ();
+	if (c == '>') {
+		/* already found match for template */
+	} else if (languageSupportsGenerics () && c != '<' && c != '=') {
+		/* this is a template */
+		cppUngetc (c);
+		skipToMatch ("<>");
+	} else if (c == '<') {
+		/* skip "<<" or "<<=". */
+		c = cppGetc ();
+		if (c != '=') {
+			cppUngetc (c);
+		}
+	} else {
+		cppUngetc (c);
+	}
+}
+
+static void parseJavaAnnotation (statementInfo *const st)
+{
+	/*
+	 * @Override
+	 * @Target(ElementType.METHOD)
+	 * @SuppressWarnings(value = "unchecked")
+	 *
+	 * But watch out for "@interface"!
+	 */
+	tokenInfo *const token = activeToken (st);
+	
+	int c = skipToNonWhite ();
+	readIdentifier (token, c);
+	if (token->keyword == KEYWORD_INTERFACE)
+	{
+		/* Oops. This was actually "@interface" defining a new annotation. */
+		processInterface (st);
+	}
+	else
+	{
+		/* Bug #1691412: skip any annotation arguments. */
+		skipParens ();
+	}
+}
+
+static int parseParens (statementInfo *const st, parenInfo *const info)
+{
+	tokenInfo *const token = activeToken (st);
+	unsigned int identifierCount = 0;
+	unsigned int depth = 1;
+	boolean firstChar = TRUE;
+	int nextChar = '\0';
+
+	CollectingSignature = TRUE;
+	vStringClear (Signature);
+	vStringPut (Signature, '(');
+	info->parameterCount = 1;
+	do
+	{
+		int c = skipToNonWhite ();
+		vStringPut (Signature, c);
+
+		switch (c)
+		{
+			case '&':
+			case '*':
+				info->isPointer = TRUE;
+				info->isKnrParamList = FALSE;
+				if (identifierCount == 0)
+					info->isParamList = FALSE;
+				initToken (token);
+				break;
+
+			case ':':
+				info->isKnrParamList = FALSE;
+				break;
+
+			case '.':
+				info->isNameCandidate = FALSE;
+				c = cppGetc ();
+				if (c != '.')
+				{
+					cppUngetc (c);
+					info->isKnrParamList = FALSE;
+				}
+				else
+				{
+					c = cppGetc ();
+					if (c != '.')
+					{
+						cppUngetc (c);
+						info->isKnrParamList = FALSE;
+					}
+					else
+						vStringCatS (Signature, "..."); /* variable arg list */
+				}
+				break;
+
+			case ',':
+				info->isNameCandidate = FALSE;
+				if (info->isKnrParamList)
+				{
+					++info->parameterCount;
+					identifierCount = 0;
+				}
+				break;
+
+			case '=':
+				info->isKnrParamList = FALSE;
+				info->isNameCandidate = FALSE;
+				if (firstChar)
+				{
+					info->isParamList = FALSE;
+					skipMacro (st);
+					depth = 0;
+				}
+				break;
+
+			case '[':
+				info->isKnrParamList = FALSE;
+				skipToMatch ("[]");
+				break;
+
+			case '<':
+				info->isKnrParamList = FALSE;
+				processAngleBracket ();
+				break;
+
+			case ')':
+				if (firstChar)
+					info->parameterCount = 0;
+				--depth;
+				break;
+
+			case '(':
+				info->isKnrParamList = FALSE;
+				if (firstChar)
+				{
+					info->isNameCandidate = FALSE;
+					cppUngetc (c);
+					vStringClear (Signature);
+					skipMacro (st);
+					depth = 0;
+					vStringChop (Signature);
+				}
+				else if (isType (token, TOKEN_PAREN_NAME))
+				{
+					c = skipToNonWhite ();
+					if (c == '*')        /* check for function pointer */
+					{
+						skipToMatch ("()");
+						c = skipToNonWhite ();
+						if (c == '(')
+							skipToMatch ("()");
+						else
+							cppUngetc (c);
+					}
+					else
+					{
+						cppUngetc (c);
+						cppUngetc ('(');
+						info->nestedArgs = TRUE;
+					}
+				}
+				else
+					++depth;
+				break;
+
+			default:
+				if (c == '@' && isLanguage (Lang_java))
+				{
+					parseJavaAnnotation(st);
+				}
+				else if (isident1 (c))
+				{
+					if (++identifierCount > 1)
+						info->isKnrParamList = FALSE;
+					readIdentifier (token, c);
+					if (isType (token, TOKEN_NAME)  &&  info->isNameCandidate)
+						token->type = TOKEN_PAREN_NAME;
+					else if (isType (token, TOKEN_KEYWORD))
+					{
+						if (token->keyword != KEYWORD_CONST &&
+							token->keyword != KEYWORD_VOLATILE)
+						{
+							info->isKnrParamList = FALSE;
+							info->isNameCandidate = FALSE;
+						}
+					}
+				}
+				else
+				{
+					info->isParamList     = FALSE;
+					info->isKnrParamList  = FALSE;
+					info->isNameCandidate = FALSE;
+					info->invalidContents = TRUE;
+				}
+				break;
+		}
+		firstChar = FALSE;
+	} while (! info->nestedArgs  &&  depth > 0  &&
+			 (info->isKnrParamList  ||  info->isNameCandidate));
+
+	if (! info->nestedArgs) while (depth > 0)
+	{
+		skipToMatch ("()");
+		--depth;
+	}
+
+	if (! info->isNameCandidate)
+		initToken (token);
+
+	vStringTerminate (Signature);
+	if (info->isKnrParamList)
+		vStringClear (Signature);
+	CollectingSignature = FALSE;
+	return nextChar;
+}
+
+static void initParenInfo (parenInfo *const info)
+{
+	info->isPointer				= FALSE;
+	info->isParamList			= TRUE;
+	info->isKnrParamList		= isLanguage (Lang_c);
+	info->isNameCandidate		= TRUE;
+	info->invalidContents		= FALSE;
+	info->nestedArgs			= FALSE;
+	info->parameterCount		= 0;
+}
+
+static void analyzeParens (statementInfo *const st)
+{
+	tokenInfo *const prev = prevToken (st, 1);
+
+	if (st->inFunction  &&  ! st->assignment)
+		st->notVariable = TRUE;
+	if (! isType (prev, TOKEN_NONE))  /* in case of ignored enclosing macros */
+	{
+		tokenInfo *const token = activeToken (st);
+		parenInfo info;
+		int c;
+
+		initParenInfo (&info);
+		parseParens (st, &info);
+		c = skipToNonWhite ();
+		cppUngetc (c);
+		if (info.invalidContents)
+			reinitStatement (st, FALSE);
+		else if (info.isNameCandidate  &&  isType (token, TOKEN_PAREN_NAME)  &&
+				 ! st->gotParenName  &&
+				 (! info.isParamList || ! st->haveQualifyingName  ||
+				  c == '('  ||
+				  (c == '='  &&  st->implementation != IMP_VIRTUAL) ||
+				  (st->declaration == DECL_NONE  &&  isOneOf (c, ",;"))))
+		{
+			token->type = TOKEN_NAME;
+			processName (st);
+			st->gotParenName = TRUE;
+			if (! (c == '('  &&  info.nestedArgs))
+				st->isPointer = info.isPointer;
+		}
+		else if (! st->gotArgs  &&  info.isParamList)
+		{
+			st->gotArgs = TRUE;
+			setToken (st, TOKEN_ARGS);
+			advanceToken (st);
+			if (st->scope != SCOPE_TYPEDEF)
+				analyzePostParens (st, &info);
+		}
+		else
+			setToken (st, TOKEN_NONE);
+	}
+}
+
+/*
+*   Token parsing functions
+*/
+
+static void addContext (statementInfo *const st, const tokenInfo* const token)
+{
+	if (isType (token, TOKEN_NAME))
+	{
+		if (vStringLength (st->context->name) > 0)
+		{
+			if (isLanguage (Lang_c)  ||  isLanguage (Lang_cpp))
+				vStringCatS (st->context->name, "::");
+			else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
+				vStringCatS (st->context->name, ".");
+		}
+		vStringCat (st->context->name, token->name);
+		st->context->type = TOKEN_NAME;
+	}
+}
+
+static boolean inheritingDeclaration (declType decl)
+{
+	/* C# supports inheritance for enums. C++0x will too, but not yet. */
+	if (decl == DECL_ENUM)
+	{
+		return (boolean) (isLanguage (Lang_csharp));
+	}
+	return (boolean) (
+		decl == DECL_CLASS ||
+		decl == DECL_STRUCT ||
+		decl == DECL_INTERFACE);
+}
+
+static void processColon (statementInfo *const st)
+{
+	int c = (isLanguage (Lang_cpp) ? cppGetc () : skipToNonWhite ());
+	const boolean doubleColon = (boolean) (c == ':');
+
+	if (doubleColon)
+	{
+		setToken (st, TOKEN_DOUBLE_COLON);
+		st->haveQualifyingName = FALSE;
+	}
+	else
+	{
+		cppUngetc (c);
+		if ((isLanguage (Lang_cpp) || isLanguage (Lang_csharp))  &&
+			inheritingDeclaration (st->declaration))
+		{
+			readParents (st, ':');
+		}
+		else if (parentDecl (st) == DECL_STRUCT)
+		{
+			c = skipToOneOf (",;");
+			if (c == ',')
+				setToken (st, TOKEN_COMMA);
+			else if (c == ';')
+				setToken (st, TOKEN_SEMICOLON);
+		}
+		else
+		{
+			const tokenInfo *const prev  = prevToken (st, 1);
+			const tokenInfo *const prev2 = prevToken (st, 2);
+			if (prev->keyword == KEYWORD_DEFAULT ||
+				prev2->keyword == KEYWORD_CASE ||
+				st->parent != NULL)
+			{
+				reinitStatement (st, FALSE);
+			}
+		}
+	}
+}
+
+/*  Skips over any initializing value which may follow an '=' character in a
+ *  variable definition.
+ */
+static int skipInitializer (statementInfo *const st)
+{
+	boolean done = FALSE;
+	int c;
+
+	while (! done)
+	{
+		c = skipToNonWhite ();
+
+		if (c == EOF)
+			longjmp (Exception, (int) ExceptionFormattingError);
+		else switch (c)
+		{
+			case ',':
+			case ';': done = TRUE; break;
+
+			case '0':
+				if (st->implementation == IMP_VIRTUAL)
+					st->implementation = IMP_PURE_VIRTUAL;
+				break;
+
+			case '[': skipToMatch ("[]"); break;
+			case '(': skipToMatch ("()"); break;
+			case '{': skipToMatch ("{}"); break;
+			case '<': processAngleBracket(); break;
+
+			case '}':
+				if (insideEnumBody (st))
+					done = TRUE;
+				else if (! isBraceFormat ())
+				{
+					verbose ("%s: unexpected closing brace at line %lu\n",
+							getInputFileName (), getInputLineNumber ());
+					longjmp (Exception, (int) ExceptionBraceFormattingError);
+				}
+				break;
+
+			default: break;
+		}
+	}
+	return c;
+}
+
+static void processInitializer (statementInfo *const st)
+{
+	const boolean inEnumBody = insideEnumBody (st);
+	int c = cppGetc ();
+
+	if (c != '=')
+	{
+		cppUngetc (c);
+		c = skipInitializer (st);
+		st->assignment = TRUE;
+		if (c == ';')
+			setToken (st, TOKEN_SEMICOLON);
+		else if (c == ',')
+			setToken (st, TOKEN_COMMA);
+		else if (c == '}'  &&  inEnumBody)
+		{
+			cppUngetc (c);
+			setToken (st, TOKEN_COMMA);
+		}
+		if (st->scope == SCOPE_EXTERN)
+			st->scope = SCOPE_GLOBAL;
+	}
+}
+
+static void parseIdentifier (statementInfo *const st, const int c)
+{
+	tokenInfo *const token = activeToken (st);
+
+	readIdentifier (token, c);
+	if (! isType (token, TOKEN_NONE))
+		processToken (token, st);
+}
+
+static void parseGeneralToken (statementInfo *const st, const int c)
+{
+	const tokenInfo *const prev = prevToken (st, 1);
+	
+	if (isident1 (c) || (isLanguage (Lang_java) && isHighChar (c)))
+	{
+		parseIdentifier (st, c);
+		if (isType (st->context, TOKEN_NAME) &&
+			isType (activeToken (st), TOKEN_NAME) && isType (prev, TOKEN_NAME))
+		{
+			initToken (st->context);
+		}
+	}
+	else if (c == '.' || c == '-')
+	{
+		if (! st->assignment)
+			st->notVariable = TRUE;
+		if (c == '-')
+		{
+			int c2 = cppGetc ();
+			if (c2 != '>')
+				cppUngetc (c2);
+		}
+	}
+	else if (c == '!' || c == '>')
+	{
+		int c2 = cppGetc ();
+		if (c2 != '=')
+			cppUngetc (c2);
+	}
+	else if (c == '@' && isLanguage (Lang_java))
+	{
+		parseJavaAnnotation (st);
+	}
+	else if (isExternCDecl (st, c))
+	{
+		st->declaration = DECL_NOMANGLE;
+		st->scope = SCOPE_GLOBAL;
+	}
+}
+
+/*  Reads characters from the pre-processor and assembles tokens, setting
+ *  the current statement state.
+ */
+static void nextToken (statementInfo *const st)
+{
+	tokenInfo *token;
+	do
+	{
+		int c = skipToNonWhite ();
+		switch (c)
+		{
+			case EOF: longjmp (Exception, (int) ExceptionEOF);  break;
+			case '(': analyzeParens (st);                       break;
+			case '<': processAngleBracket ();                   break;
+			case '*': st->haveQualifyingName = FALSE;           break;
+			case ',': setToken (st, TOKEN_COMMA);               break;
+			case ':': processColon (st);                        break;
+			case ';': setToken (st, TOKEN_SEMICOLON);           break;
+			case '=': processInitializer (st);                  break;
+			case '[': skipToMatch ("[]");                       break;
+			case '{': setToken (st, TOKEN_BRACE_OPEN);          break;
+			case '}': setToken (st, TOKEN_BRACE_CLOSE);         break;
+			default:  parseGeneralToken (st, c);                break;
+		}
+		token = activeToken (st);
+	} while (isType (token, TOKEN_NONE));
+}
+
+/*
+*   Scanning support functions
+*/
+
+static statementInfo *CurrentStatement = NULL;
+
+static statementInfo *newStatement (statementInfo *const parent)
+{
+	statementInfo *const st = xMalloc (1, statementInfo);
+	unsigned int i;
+
+	for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
+		st->token [i] = newToken ();
+
+	st->context = newToken ();
+	st->blockName = newToken ();
+	st->parentClasses = vStringNew ();
+
+	initStatement (st, parent);
+	CurrentStatement = st;
+
+	return st;
+}
+
+static void deleteStatement (void)
+{
+	statementInfo *const st = CurrentStatement;
+	statementInfo *const parent = st->parent;
+	unsigned int i;
+
+	for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
+	{
+		deleteToken (st->token [i]);       st->token [i] = NULL;
+	}
+	deleteToken (st->blockName);           st->blockName = NULL;
+	deleteToken (st->context);             st->context = NULL;
+	vStringDelete (st->parentClasses);     st->parentClasses = NULL;
+	eFree (st);
+	CurrentStatement = parent;
+}
+
+static void deleteAllStatements (void)
+{
+	while (CurrentStatement != NULL)
+		deleteStatement ();
+}
+
+static boolean isStatementEnd (const statementInfo *const st)
+{
+	const tokenInfo *const token = activeToken (st);
+	boolean isEnd;
+
+	if (isType (token, TOKEN_SEMICOLON))
+		isEnd = TRUE;
+	else if (isType (token, TOKEN_BRACE_CLOSE))
+		/* Java and C# do not require semicolons to end a block. Neither do C++
+		 * namespaces. All other blocks require a semicolon to terminate them.
+		 */
+		isEnd = (boolean) (isLanguage (Lang_java) || isLanguage (Lang_csharp) ||
+				! isContextualStatement (st));
+	else
+		isEnd = FALSE;
+
+	return isEnd;
+}
+
+static void checkStatementEnd (statementInfo *const st)
+{
+	const tokenInfo *const token = activeToken (st);
+
+	if (isType (token, TOKEN_COMMA))
+		reinitStatement (st, TRUE);
+	else if (isStatementEnd (st))
+	{
+		DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>"); )
+		reinitStatement (st, FALSE);
+		cppEndStatement ();
+	}
+	else
+	{
+		cppBeginStatement ();
+		advanceToken (st);
+	}
+}
+
+static void nest (statementInfo *const st, const unsigned int nestLevel)
+{
+	switch (st->declaration)
+	{
+		case DECL_CLASS:
+		case DECL_ENUM:
+		case DECL_INTERFACE:
+		case DECL_NAMESPACE:
+		case DECL_NOMANGLE:
+		case DECL_STRUCT:
+		case DECL_UNION:
+			createTags (nestLevel, st);
+			break;
+
+		case DECL_FUNCTION:
+		case DECL_TASK:
+			st->inFunction = TRUE;
+			/* fall through */
+		default:
+			if (includeTag (TAG_LOCAL, FALSE))
+				createTags (nestLevel, st);
+			else
+				skipToMatch ("{}");
+			break;
+	}
+	advanceToken (st);
+	setToken (st, TOKEN_BRACE_CLOSE);
+}
+
+static void tagCheck (statementInfo *const st)
+{
+	const tokenInfo *const token = activeToken (st);
+	const tokenInfo *const prev  = prevToken (st, 1);
+	const tokenInfo *const prev2 = prevToken (st, 2);
+
+	switch (token->type)
+	{
+		case TOKEN_NAME:
+			if (insideEnumBody (st))
+				qualifyEnumeratorTag (st, token);
+			break;
+#if 0
+		case TOKEN_PACKAGE:
+			if (st->haveQualifyingName)
+				makeTag (token, st, FALSE, TAG_PACKAGE);
+			break;
+#endif
+		case TOKEN_BRACE_OPEN:
+			if (isType (prev, TOKEN_ARGS))
+			{
+				if (st->haveQualifyingName)
+				{
+					if (! isLanguage (Lang_vera))
+						st->declaration = DECL_FUNCTION;
+					if (isType (prev2, TOKEN_NAME))
+						copyToken (st->blockName, prev2);
+					qualifyFunctionTag (st, prev2);
+				}
+			}
+			else if (isContextualStatement (st) ||
+					st->declaration == DECL_NAMESPACE ||
+					st->declaration == DECL_PROGRAM)
+			{
+				if (isType (prev, TOKEN_NAME))
+					copyToken (st->blockName, prev);
+				else
+				{
+					/*  For an anonymous struct or union we use a unique ID
+					 *  a number, so that the members can be found.
+					 */
+					char buf [20];  /* length of "_anon" + digits  + null */
+					sprintf (buf, "__anon%d", ++AnonymousID);
+					vStringCopyS (st->blockName->name, buf);
+					st->blockName->type = TOKEN_NAME;
+					st->blockName->keyword = KEYWORD_NONE;
+				}
+				qualifyBlockTag (st, prev);
+			}
+			else if (isLanguage (Lang_csharp))
+				makeTag (prev, st, FALSE, TAG_PROPERTY);
+			break;
+
+		case TOKEN_SEMICOLON:
+		case TOKEN_COMMA:
+			if (insideEnumBody (st))
+				;
+			else if (isType (prev, TOKEN_NAME))
+			{
+				if (isContextualKeyword (prev2))
+					makeTag (prev, st, TRUE, TAG_EXTERN_VAR);
+				else
+					qualifyVariableTag (st, prev);
+			}
+			else if (isType (prev, TOKEN_ARGS)  &&  isType (prev2, TOKEN_NAME))
+			{
+				if (st->isPointer)
+					qualifyVariableTag (st, prev2);
+				else
+					qualifyFunctionDeclTag (st, prev2);
+			}
+			if (isLanguage (Lang_java) && token->type == TOKEN_SEMICOLON && insideEnumBody (st))
+			{
+				/* In Java, after an initial enum-like part,
+				 * a semicolon introduces a class-like part.
+				 * See Bug #1730485 for the full rationale. */
+				st->parent->declaration = DECL_CLASS;
+			}
+			break;
+
+		default: break;
+	}
+}
+
+/*  Parses the current file and decides whether to write out and tags that
+ *  are discovered.
+ */
+static void createTags (const unsigned int nestLevel,
+						statementInfo *const parent)
+{
+	statementInfo *const st = newStatement (parent);
+
+	DebugStatement ( if (nestLevel > 0) debugParseNest (TRUE, nestLevel); )
+	while (TRUE)
+	{
+		tokenInfo *token;
+
+		nextToken (st);
+		token = activeToken (st);
+		if (isType (token, TOKEN_BRACE_CLOSE))
+		{
+			if (nestLevel > 0)
+				break;
+			else
+			{
+				verbose ("%s: unexpected closing brace at line %lu\n",
+						getInputFileName (), getInputLineNumber ());
+				longjmp (Exception, (int) ExceptionBraceFormattingError);
+			}
+		}
+		else if (isType (token, TOKEN_DOUBLE_COLON))
+		{
+			addContext (st, prevToken (st, 1));
+			advanceToken (st);
+		}
+		else
+		{
+			tagCheck (st);
+			if (isType (token, TOKEN_BRACE_OPEN))
+				nest (st, nestLevel + 1);
+			checkStatementEnd (st);
+		}
+	}
+	deleteStatement ();
+	DebugStatement ( if (nestLevel > 0) debugParseNest (FALSE, nestLevel - 1); )
+}
+
+static boolean findCTags (const unsigned int passCount)
+{
+	exception_t exception;
+	boolean retry;
+
+	Assert (passCount < 3);
+	cppInit ((boolean) (passCount > 1), isLanguage (Lang_csharp));
+	Signature = vStringNew ();
+
+	exception = (exception_t) setjmp (Exception);
+	retry = FALSE;
+	if (exception == ExceptionNone)
+		createTags (0, NULL);
+	else
+	{
+		deleteAllStatements ();
+		if (exception == ExceptionBraceFormattingError  &&  passCount == 1)
+		{
+			retry = TRUE;
+		   verbose ("%s: retrying file with fallback brace matching algorithm\n",
+					getInputFileName ());
+		}
+	}
+	vStringDelete (Signature);
+	cppTerminate ();
+	return retry;
+}
+
+static void buildKeywordHash (const langType language, unsigned int idx)
+{
+	const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
+	size_t i;
+	for (i = 0  ;  i < count  ;  ++i)
+	{
+		const keywordDesc* const p = &KeywordTable [i];
+		if (p->isValid [idx])
+			addKeyword (p->name, language, (int) p->id);
+	}
+}
+
+static void initializeCParser (const langType language)
+{
+	Lang_c = language;
+	buildKeywordHash (language, 0);
+}
+
+static void initializeCppParser (const langType language)
+{
+	Lang_cpp = language;
+	buildKeywordHash (language, 1);
+}
+
+static void initializeCsharpParser (const langType language)
+{
+	Lang_csharp = language;
+	buildKeywordHash (language, 2);
+}
+
+static void initializeJavaParser (const langType language)
+{
+	Lang_java = language;
+	buildKeywordHash (language, 3);
+}
+
+static void initializeVeraParser (const langType language)
+{
+	Lang_vera = language;
+	buildKeywordHash (language, 4);
+}
+
+extern parserDefinition* CParser (void)
+{
+	static const char *const extensions [] = { "c", NULL };
+	parserDefinition* def = parserNew ("C");
+	def->kinds      = CKinds;
+	def->kindCount  = KIND_COUNT (CKinds);
+	def->extensions = extensions;
+	def->parser2    = findCTags;
+	def->initialize = initializeCParser;
+	return def;
+}
+
+extern parserDefinition* CppParser (void)
+{
+	static const char *const extensions [] = {
+		"c++", "cc", "cp", "cpp", "cxx", "h", "h++", "hh", "hp", "hpp", "hxx",
+#ifndef CASE_INSENSITIVE_FILENAMES
+		"C", "H",
+#endif
+		NULL
+	};
+	parserDefinition* def = parserNew ("C++");
+	def->kinds      = CKinds;
+	def->kindCount  = KIND_COUNT (CKinds);
+	def->extensions = extensions;
+	def->parser2    = findCTags;
+	def->initialize = initializeCppParser;
+	return def;
+}
+
+extern parserDefinition* CsharpParser (void)
+{
+	static const char *const extensions [] = { "cs", NULL };
+	parserDefinition* def = parserNew ("C#");
+	def->kinds      = CsharpKinds;
+	def->kindCount  = KIND_COUNT (CsharpKinds);
+	def->extensions = extensions;
+	def->parser2    = findCTags;
+	def->initialize = initializeCsharpParser;
+	return def;
+}
+
+extern parserDefinition* JavaParser (void)
+{
+	static const char *const extensions [] = { "java", NULL };
+	parserDefinition* def = parserNew ("Java");
+	def->kinds      = JavaKinds;
+	def->kindCount  = KIND_COUNT (JavaKinds);
+	def->extensions = extensions;
+	def->parser2    = findCTags;
+	def->initialize = initializeJavaParser;
+	return def;
+}
+
+extern parserDefinition* VeraParser (void)
+{
+	static const char *const extensions [] = { "vr", "vri", "vrh", NULL };
+	parserDefinition* def = parserNew ("Vera");
+	def->kinds      = VeraKinds;
+	def->kindCount  = KIND_COUNT (VeraKinds);
+	def->extensions = extensions;
+	def->parser2    = findCTags;
+	def->initialize = initializeVeraParser;
+	return def;
+}
+
+/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/eiffel.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/eiffel.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/eiffel.c	(revision 5)
@@ -0,0 +1,1352 @@
+/*
+*   $Id: eiffel.c 706 2009-06-28 23:09:30Z dhiebert $
+*
+*   Copyright (c) 1998-2002, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions for generating tags for Eiffel language
+*   files.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#ifdef TYPE_REFERENCE_TOOL
+#include <stdio.h>
+#endif
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>  /* to define tolower () */
+#include <setjmp.h>
+
+#include "debug.h"
+#include "keyword.h"
+#include "routines.h"
+#include "vstring.h"
+#ifndef TYPE_REFERENCE_TOOL
+#include "entry.h"
+#include "options.h"
+#include "parse.h"
+#include "read.h"
+#endif
+
+/*
+*   MACROS
+*/
+#define isident(c)            (isalnum(c) || (c) == '_')
+#define isFreeOperatorChar(c) ((c) == '@' || (c) == '#' || \
+                               (c) == '|' || (c) == '&')
+#define isType(token,t)       (boolean) ((token)->type == (t))
+#define isKeyword(token,k)    (boolean) ((token)->keyword == (k))
+
+/*
+*   DATA DECLARATIONS
+*/
+
+typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
+
+/*  Used to specify type of keyword.
+ */
+typedef enum eKeywordId {
+	KEYWORD_NONE = -1,
+	KEYWORD_alias, KEYWORD_all, KEYWORD_and, KEYWORD_as, KEYWORD_assign,
+	KEYWORD_check, KEYWORD_class, KEYWORD_convert, KEYWORD_create,
+	KEYWORD_creation, KEYWORD_Current,
+	KEYWORD_debug, KEYWORD_deferred, KEYWORD_do, KEYWORD_else,
+	KEYWORD_elseif, KEYWORD_end, KEYWORD_ensure, KEYWORD_expanded,
+	KEYWORD_export, KEYWORD_external, KEYWORD_false, KEYWORD_feature,
+	KEYWORD_from, KEYWORD_frozen, KEYWORD_if, KEYWORD_implies,
+	KEYWORD_indexing, KEYWORD_infix, KEYWORD_inherit, KEYWORD_inspect,
+	KEYWORD_invariant, KEYWORD_is, KEYWORD_like, KEYWORD_local,
+	KEYWORD_loop, KEYWORD_not, KEYWORD_obsolete, KEYWORD_old, KEYWORD_once,
+	KEYWORD_or, KEYWORD_prefix, KEYWORD_redefine, KEYWORD_rename,
+	KEYWORD_require, KEYWORD_rescue, KEYWORD_Result, KEYWORD_retry,
+	KEYWORD_select, KEYWORD_separate, KEYWORD_strip, KEYWORD_then,
+	KEYWORD_true, KEYWORD_undefine, KEYWORD_unique, KEYWORD_until,
+	KEYWORD_variant, KEYWORD_when, KEYWORD_xor
+} keywordId;
+
+/*  Used to determine whether keyword is valid for the token language and
+ *  what its ID is.
+ */
+typedef struct sKeywordDesc {
+	const char *name;
+	keywordId id;
+} keywordDesc;
+
+typedef enum eTokenType {
+	TOKEN_UNDEFINED,
+	TOKEN_BANG,
+	TOKEN_CHARACTER,
+	TOKEN_CLOSE_BRACE,
+	TOKEN_CLOSE_BRACKET,
+	TOKEN_CLOSE_PAREN,
+	TOKEN_COLON,
+	TOKEN_COMMA,
+	TOKEN_CONSTRAINT,
+	TOKEN_DOT,
+	TOKEN_DOLLAR,
+	TOKEN_IDENTIFIER,
+	TOKEN_KEYWORD,
+	TOKEN_NUMERIC,
+	TOKEN_OPEN_BRACE,
+	TOKEN_OPEN_BRACKET,
+	TOKEN_OPEN_PAREN,
+	TOKEN_OPERATOR,
+	TOKEN_OTHER,
+	TOKEN_QUESTION,
+	TOKEN_SEMICOLON,
+	TOKEN_SEPARATOR,
+	TOKEN_STRING,
+	TOKEN_TILDE
+} tokenType;
+
+typedef struct sTokenInfo {
+	tokenType type;
+	keywordId keyword;
+	boolean   isExported;
+	vString*  string;
+	vString*  className;
+	vString*  featureName;
+} tokenInfo;
+
+/*
+*   DATA DEFINITIONS
+*/
+
+static langType Lang_eiffel;
+
+#ifdef TYPE_REFERENCE_TOOL
+
+static const char *FileName;
+static FILE *File;
+static int PrintClass;
+static int PrintReferences;
+static int SelfReferences;
+static int Debug;
+static stringList *GenericNames;
+static stringList *ReferencedTypes;
+
+#else
+
+typedef enum {
+	EKIND_CLASS, EKIND_FEATURE, EKIND_LOCAL, EKIND_QUALIFIED_TAGS
+} eiffelKind;
+
+static kindOption EiffelKinds [] = {
+	{ TRUE,  'c', "class",   "classes"},
+	{ TRUE,  'f', "feature", "features"},
+	{ FALSE, 'l', "local",   "local entities"}
+};
+
+#endif
+
+static jmp_buf Exception;
+
+static const keywordDesc EiffelKeywordTable [] = {
+	/* keyword          keyword ID */
+	{ "alias",          KEYWORD_alias      },
+	{ "all",            KEYWORD_all        },
+	{ "and",            KEYWORD_and        },
+	{ "as",             KEYWORD_as         },
+	{ "assign",         KEYWORD_assign     },
+	{ "check",          KEYWORD_check      },
+	{ "class",          KEYWORD_class      },
+	{ "convert",        KEYWORD_convert    },
+	{ "create",         KEYWORD_create     },
+	{ "creation",       KEYWORD_creation   },
+	{ "current",        KEYWORD_Current    },
+	{ "debug",          KEYWORD_debug      },
+	{ "deferred",       KEYWORD_deferred   },
+	{ "do",             KEYWORD_do         },
+	{ "else",           KEYWORD_else       },
+	{ "elseif",         KEYWORD_elseif     },
+	{ "end",            KEYWORD_end        },
+	{ "ensure",         KEYWORD_ensure     },
+	{ "expanded",       KEYWORD_expanded   },
+	{ "export",         KEYWORD_export     },
+	{ "external",       KEYWORD_external   },
+	{ "false",          KEYWORD_false      },
+	{ "feature",        KEYWORD_feature    },
+	{ "from",           KEYWORD_from       },
+	{ "frozen",         KEYWORD_frozen     },
+	{ "if",             KEYWORD_if         },
+	{ "implies",        KEYWORD_implies    },
+	{ "indexing",       KEYWORD_indexing   },
+	{ "infix",          KEYWORD_infix      },
+	{ "inherit",        KEYWORD_inherit    },
+	{ "inspect",        KEYWORD_inspect    },
+	{ "invariant",      KEYWORD_invariant  },
+	{ "is",             KEYWORD_is         },
+	{ "like",           KEYWORD_like       },
+	{ "local",          KEYWORD_local      },
+	{ "loop",           KEYWORD_loop       },
+	{ "not",            KEYWORD_not        },
+	{ "obsolete",       KEYWORD_obsolete   },
+	{ "old",            KEYWORD_old        },
+	{ "once",           KEYWORD_once       },
+	{ "or",             KEYWORD_or         },
+	{ "prefix",         KEYWORD_prefix     },
+	{ "redefine",       KEYWORD_redefine   },
+	{ "rename",         KEYWORD_rename     },
+	{ "require",        KEYWORD_require    },
+	{ "rescue",         KEYWORD_rescue     },
+	{ "result",         KEYWORD_Result     },
+	{ "retry",          KEYWORD_retry      },
+	{ "select",         KEYWORD_select     },
+	{ "separate",       KEYWORD_separate   },
+	{ "strip",          KEYWORD_strip      },
+	{ "then",           KEYWORD_then       },
+	{ "true",           KEYWORD_true       },
+	{ "undefine",       KEYWORD_undefine   },
+	{ "unique",         KEYWORD_unique     },
+	{ "until",          KEYWORD_until      },
+	{ "variant",        KEYWORD_variant    },
+	{ "when",           KEYWORD_when       },
+	{ "xor",            KEYWORD_xor        }
+};
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+static void buildEiffelKeywordHash (void)
+{
+	const size_t count = sizeof (EiffelKeywordTable) /
+						 sizeof (EiffelKeywordTable [0]);
+	size_t i;
+	for (i = 0  ;  i < count  ;  ++i)
+	{
+		const keywordDesc* const p = &EiffelKeywordTable [i];
+		addKeyword (p->name, Lang_eiffel, (int) p->id);
+	}
+}
+
+#ifdef TYPE_REFERENCE_TOOL
+
+static void addGenericName (tokenInfo *const token)
+{
+	vStringUpper (token->string);
+	if (vStringLength (token->string) > 0)
+		stringListAdd (GenericNames, vStringNewCopy (token->string));
+}
+
+static boolean isGeneric (tokenInfo *const token)
+{
+	return (boolean) stringListHas (GenericNames, vStringValue (token->string));
+}
+
+static void reportType (tokenInfo *const token)
+{
+	vStringUpper (token->string);
+	if (vStringLength (token->string) > 0  && ! isGeneric (token)  &&
+		(SelfReferences || strcmp (vStringValue (
+			token->string), vStringValue (token->className)) != 0) &&
+		! stringListHas (ReferencedTypes, vStringValue (token->string)))
+	{
+		printf ("%s\n", vStringValue (token->string));
+		stringListAdd (ReferencedTypes, vStringNewCopy (token->string));
+	}
+}
+
+static int fileGetc (void)
+{
+	int c = getc (File);
+	if (c == '\r')
+	{
+		c = getc (File);
+		if (c != '\n')
+		{
+			ungetc (c, File);
+			c = '\n';
+		}
+	}
+	if (Debug > 0  &&  c != EOF)
+		putc (c, errout);
+	return c;
+}
+
+static int fileUngetc (c)
+{
+	return ungetc (c, File);
+}
+
+extern char *readLine (vString *const vLine, FILE *const fp)
+{
+	return NULL;
+}
+
+#else
+
+/*
+*   Tag generation functions
+*/
+
+static void makeEiffelClassTag (tokenInfo *const token)
+{
+	if (EiffelKinds [EKIND_CLASS].enabled)
+	{
+		const char *const name = vStringValue (token->string);
+		tagEntryInfo e;
+
+		initTagEntry (&e, name);
+
+		e.kindName = EiffelKinds [EKIND_CLASS].name;
+		e.kind     = EiffelKinds [EKIND_CLASS].letter;
+
+		makeTagEntry (&e);
+	}
+	vStringCopy (token->className, token->string);
+}
+
+static void makeEiffelFeatureTag (tokenInfo *const token)
+{
+	if (EiffelKinds [EKIND_FEATURE].enabled  &&
+		(token->isExported  ||  Option.include.fileScope))
+	{
+		const char *const name = vStringValue (token->string);
+		tagEntryInfo e;
+
+		initTagEntry (&e, name);
+
+		e.isFileScope = (boolean) (! token->isExported);
+		e.kindName    = EiffelKinds [EKIND_FEATURE].name;
+		e.kind        = EiffelKinds [EKIND_FEATURE].letter;
+		e.extensionFields.scope [0] = EiffelKinds [EKIND_CLASS].name;
+		e.extensionFields.scope [1] = vStringValue (token->className);
+
+		makeTagEntry (&e);
+
+		if (Option.include.qualifiedTags)
+		{
+			vString* qualified = vStringNewInit (vStringValue (token->className));
+			vStringPut (qualified, '.');
+			vStringCat (qualified, token->string);
+			e.name = vStringValue (qualified);
+			makeTagEntry (&e);
+			vStringDelete (qualified);
+		}
+	}
+	vStringCopy (token->featureName, token->string);
+}
+
+static void makeEiffelLocalTag (tokenInfo *const token)
+{
+	if (EiffelKinds [EKIND_LOCAL].enabled && Option.include.fileScope)
+	{
+		const char *const name = vStringValue (token->string);
+		vString* scope = vStringNew ();
+		tagEntryInfo e;
+
+		initTagEntry (&e, name);
+
+		e.isFileScope = TRUE;
+		e.kindName    = EiffelKinds [EKIND_LOCAL].name;
+		e.kind        = EiffelKinds [EKIND_LOCAL].letter;
+
+		vStringCopy (scope, token->className);
+		vStringPut (scope, '.');
+		vStringCat (scope, token->featureName);
+
+		e.extensionFields.scope [0] = EiffelKinds [EKIND_FEATURE].name;
+		e.extensionFields.scope [1] = vStringValue (scope);
+
+		makeTagEntry (&e);
+		vStringDelete (scope);
+	}
+}
+
+#endif
+
+/*
+*   Parsing functions
+*/
+
+static int skipToCharacter (const int c)
+{
+	int d;
+
+	do
+	{
+		d = fileGetc ();
+	} while (d != EOF  &&  d != c);
+
+	return d;
+}
+
+/*  If a numeric is passed in 'c', this is used as the first digit of the
+ *  numeric being parsed.
+ */
+static vString *parseInteger (int c)
+{
+	vString *string = vStringNew ();
+
+	if (c == '\0')
+		c = fileGetc ();
+	if (c == '-')
+	{
+		vStringPut (string, c);
+		c = fileGetc ();
+	}
+	else if (! isdigit (c))
+		c = fileGetc ();
+	while (c != EOF  &&  (isdigit (c)  ||  c == '_'))
+	{
+		vStringPut (string, c);
+		c = fileGetc ();
+	}
+	vStringTerminate (string);
+	fileUngetc (c);
+
+	return string;
+}
+
+static vString *parseNumeric (int c)
+{
+	vString *string = vStringNew ();
+	vString *integer = parseInteger (c);
+	vStringCopy (string, integer);
+	vStringDelete (integer);
+
+	c = fileGetc ();
+	if (c == '.')
+	{
+		integer = parseInteger ('\0');
+		vStringPut (string, c);
+		vStringCat (string, integer);
+		vStringDelete (integer);
+		c = fileGetc ();
+	}
+	if (tolower (c) == 'e')
+	{
+		integer = parseInteger ('\0');
+		vStringPut (string, c);
+		vStringCat (string, integer);
+		vStringDelete (integer);
+	}
+	else if (!isspace (c))
+		fileUngetc (c);
+
+	vStringTerminate (string);
+
+	return string;
+}
+
+static int parseEscapedCharacter (void)
+{
+	int d = '\0';
+	int c = fileGetc ();
+
+	switch (c)
+	{
+		case 'A':  d = '@';   break;
+		case 'B':  d = '\b';  break;
+		case 'C':  d = '^';   break;
+		case 'D':  d = '$';   break;
+		case 'F':  d = '\f';  break;
+		case 'H':  d = '\\';  break;
+		case 'L':  d = '~';   break;
+		case 'N':  d = '\n';  break;
+#ifdef QDOS
+		case 'Q':  d = 0x9F;  break;
+#else
+		case 'Q':  d = '`';   break;
+#endif
+		case 'R':  d = '\r';  break;
+		case 'S':  d = '#';   break;
+		case 'T':  d = '\t';  break;
+		case 'U':  d = '\0';  break;
+		case 'V':  d = '|';   break;
+		case '%':  d = '%';   break;
+		case '\'': d = '\'';  break;
+		case '"':  d = '"';   break;
+		case '(':  d = '[';   break;
+		case ')':  d = ']';   break;
+		case '<':  d = '{';   break;
+		case '>':  d = '}';   break;
+
+		case '\n': skipToCharacter ('%'); break;
+
+		case '/':
+		{
+			vString *string = parseInteger ('\0');
+			const char *value = vStringValue (string);
+			const unsigned long ascii = atol (value);
+			vStringDelete (string);
+
+			c = fileGetc ();
+			if (c == '/'  &&  ascii < 256)
+				d = ascii;
+			break;
+		}
+
+		default: break;
+	}
+	return d;
+}
+
+static int parseCharacter (void)
+{
+	int c = fileGetc ();
+	int result = c;
+
+	if (c == '%')
+		result = parseEscapedCharacter ();
+
+	c = fileGetc ();
+	if (c != '\'')
+		skipToCharacter ('\n');
+
+	return result;
+}
+
+static void parseString (vString *const string)
+{
+	boolean verbatim = FALSE;
+	boolean align = FALSE;
+	boolean end = FALSE;
+	vString *verbatimCloser = vStringNew ();
+	vString *lastLine = vStringNew ();
+	int prev = '\0';
+	int c;
+
+	while (! end)
+	{
+		c = fileGetc ();
+		if (c == EOF)
+			end = TRUE;
+		else if (c == '"')
+		{
+			if (! verbatim)
+				end = TRUE;
+			else
+				end = (boolean) (strcmp (vStringValue (lastLine),
+				                         vStringValue (verbatimCloser)) == 0);
+		}
+		else if (c == '\n')
+		{
+			if (verbatim)
+				vStringClear (lastLine);
+			if (prev == '[' /* ||  prev == '{' */)
+			{
+				verbatim = TRUE;
+				vStringClear (verbatimCloser);
+				vStringClear (lastLine);
+				if (prev == '{')
+					vStringPut (verbatimCloser, '}');
+				else
+				{
+					vStringPut (verbatimCloser, ']');
+					align = TRUE;
+				}
+				vStringNCat (verbatimCloser, string, vStringLength (string) - 1);
+				vStringClear (string);
+			}
+			if (verbatim && align)
+			{
+				do
+					c = fileGetc ();
+				while (isspace (c));
+			}
+		}
+		else if (c == '%')
+			c = parseEscapedCharacter ();
+		if (! end)
+		{
+			vStringPut (string, c);
+			if (verbatim)
+			{
+				vStringPut (lastLine, c);
+				vStringTerminate (lastLine);
+			}
+			prev = c;
+		}
+	}
+	vStringTerminate (string);
+	vStringDelete (lastLine);
+	vStringDelete (verbatimCloser);
+}
+
+/*  Read a C identifier beginning with "firstChar" and places it into "name".
+ */
+static void parseIdentifier (vString *const string, const int firstChar)
+{
+	int c = firstChar;
+
+	do
+	{
+		vStringPut (string, c);
+		c = fileGetc ();
+	} while (isident (c));
+
+	vStringTerminate (string);
+	if (!isspace (c))
+		fileUngetc (c);  /* unget non-identifier character */
+}
+
+static void parseFreeOperator (vString *const string, const int firstChar)
+{
+	int c = firstChar;
+
+	do
+	{
+		vStringPut (string, c);
+		c = fileGetc ();
+	} while (c > ' ');
+
+	vStringTerminate (string);
+	if (!isspace (c))
+		fileUngetc (c);  /* unget non-identifier character */
+}
+
+static void copyToken (tokenInfo* dst, const tokenInfo *src)
+{
+	dst->type       = src->type;
+	dst->keyword    = src->keyword;
+	dst->isExported = src->isExported;
+
+	vStringCopy (dst->string, src->string);
+	vStringCopy (dst->className, src->className);
+	vStringCopy (dst->featureName, src->featureName);
+}
+
+static tokenInfo *newToken (void)
+{
+	tokenInfo *const token = xMalloc (1, tokenInfo);
+
+	token->type			= TOKEN_UNDEFINED;
+	token->keyword		= KEYWORD_NONE;
+	token->isExported	= TRUE;
+
+	token->string = vStringNew ();
+	token->className = vStringNew ();
+	token->featureName = vStringNew ();
+
+	return token;
+}
+
+static void deleteToken (tokenInfo *const token)
+{
+	vStringDelete (token->string);
+	vStringDelete (token->className);
+	vStringDelete (token->featureName);
+
+	eFree (token);
+}
+
+static void readToken (tokenInfo *const token)
+{
+	int c;
+
+	token->type    = TOKEN_UNDEFINED;
+	token->keyword = KEYWORD_NONE;
+	vStringClear (token->string);
+
+getNextChar:
+
+	do
+		c = fileGetc ();
+	while (c == '\t'  ||  c == ' '  ||  c == '\n');
+
+	switch (c)
+	{
+		case EOF:  longjmp (Exception, (int)ExceptionEOF); break;
+		case ';':  token->type = TOKEN_SEMICOLON;          break;
+		case '!':  token->type = TOKEN_BANG;               break;
+		case '}':  token->type = TOKEN_CLOSE_BRACE;        break;
+		case ']':  token->type = TOKEN_CLOSE_BRACKET;      break;
+		case ')':  token->type = TOKEN_CLOSE_PAREN;        break;
+		case ',':  token->type = TOKEN_COMMA;              break;
+		case '$':  token->type = TOKEN_DOLLAR;             break;
+		case '.':  token->type = TOKEN_DOT;                break;
+		case '{':  token->type = TOKEN_OPEN_BRACE;         break;
+		case '[':  token->type = TOKEN_OPEN_BRACKET;       break;
+		case '(':  token->type = TOKEN_OPEN_PAREN;         break;
+		case '~':  token->type = TOKEN_TILDE;              break;
+
+
+		case '+':
+		case '*':
+		case '^':
+		case '=':  token->type = TOKEN_OPERATOR;           break;
+
+		case '-':
+			c = fileGetc ();
+			if (c == '>')
+				token->type = TOKEN_CONSTRAINT;
+			else if (c == '-')  /* is this the start of a comment? */
+			{
+				skipToCharacter ('\n');
+				goto getNextChar;
+			}
+			else
+			{
+				if (!isspace (c))
+					fileUngetc (c);
+				token->type = TOKEN_OPERATOR;
+			}
+			break;
+
+		case '?':
+		case ':':
+		{
+			int c2 = fileGetc ();
+			if (c2 == '=')
+				token->type = TOKEN_OPERATOR;
+			else
+			{
+				if (!isspace (c2))
+					fileUngetc (c2);
+				if (c == ':')
+					token->type = TOKEN_COLON;
+				else
+					token->type = TOKEN_QUESTION;
+			}
+			break;
+		}
+
+		case '<':
+			c = fileGetc ();
+			if (c != '='  &&  c != '>'  &&  !isspace (c))
+				fileUngetc (c);
+			token->type = TOKEN_OPERATOR;
+			break;
+
+		case '>':
+			c = fileGetc ();
+			if (c != '='  &&  c != '>'  &&  !isspace (c))
+				fileUngetc (c);
+			token->type = TOKEN_OPERATOR;
+			break;
+
+		case '/':
+			c = fileGetc ();
+			if (c != '/'  &&  c != '='  &&  !isspace (c))
+				fileUngetc (c);
+			token->type = TOKEN_OPERATOR;
+			break;
+
+		case '\\':
+			c = fileGetc ();
+			if (c != '\\'  &&  !isspace (c))
+				fileUngetc (c);
+			token->type = TOKEN_OPERATOR;
+			break;
+
+		case '"':
+			token->type = TOKEN_STRING;
+			parseString (token->string);
+			break;
+
+		case '\'':
+			token->type = TOKEN_CHARACTER;
+			parseCharacter ();
+			break;
+
+		default:
+			if (isalpha (c))
+			{
+				parseIdentifier (token->string, c);
+				token->keyword = analyzeToken (token->string, Lang_eiffel);
+				if (isKeyword (token, KEYWORD_NONE))
+					token->type = TOKEN_IDENTIFIER;
+				else
+					token->type = TOKEN_KEYWORD;
+			}
+			else if (isdigit (c))
+			{
+				vString* numeric = parseNumeric (c);
+				vStringCat (token->string, numeric);
+				vStringDelete (numeric);
+				token->type = TOKEN_NUMERIC;
+			}
+			else if (isFreeOperatorChar (c))
+			{
+				parseFreeOperator (token->string, c);
+				token->type = TOKEN_OPERATOR;
+			}
+			else
+			{
+				token->type = TOKEN_UNDEFINED;
+				Assert (! isType (token, TOKEN_UNDEFINED));
+			}
+			break;
+	}
+}
+
+/*
+*   Scanning functions
+*/
+
+static boolean isIdentifierMatch (
+		const tokenInfo *const token, const char *const name)
+{
+	return (boolean) (isType (token, TOKEN_IDENTIFIER)  &&
+		strcasecmp (vStringValue (token->string), name) == 0);
+}
+
+static void findToken (tokenInfo *const token, const tokenType type)
+{
+	while (! isType (token, type))
+		readToken (token);
+}
+
+static void findKeyword (tokenInfo *const token, const keywordId keyword)
+{
+	while (! isKeyword (token, keyword))
+		readToken (token);
+}
+
+static boolean parseType (tokenInfo *const token);
+
+static void parseGeneric (tokenInfo *const token, boolean declaration)
+{
+	unsigned int depth = 0;
+#ifdef TYPE_REFERENCE_TOOL
+	boolean constraint = FALSE;
+#endif
+	Assert (isType (token, TOKEN_OPEN_BRACKET));
+	do
+	{
+		if (isType (token, TOKEN_OPEN_BRACKET))
+		{
+			++depth;
+			readToken (token);
+		}
+		else if (isType (token, TOKEN_CLOSE_BRACKET))
+		{
+			--depth;
+			readToken (token);
+		}
+#ifdef TYPE_REFERENCE_TOOL
+		else if (declaration)
+		{
+			boolean advanced = FALSE;
+			if (depth == 1)
+			{
+				if (isType (token, TOKEN_CONSTRAINT))
+					constraint = TRUE;
+				else if (isKeyword (token, KEYWORD_create))
+					findKeyword (token, KEYWORD_end);
+				else if (isType (token, TOKEN_IDENTIFIER))
+				{
+					if (constraint)
+						advanced = parseType (token);
+					else
+						addGenericName (token);
+					constraint = FALSE;
+				}
+			}
+			else if (isType (token, TOKEN_IDENTIFIER))
+				advanced = parseType (token);
+			if (! advanced)
+				readToken (token);
+		}
+#endif
+		else
+			parseType (token);
+	} while (depth > 0);
+}
+
+static boolean parseType (tokenInfo *const token)
+{
+	tokenInfo* const id = newToken ();
+	copyToken (id, token);
+	readToken (token);
+	if (isType (token, TOKEN_COLON))  /* check for "{entity: TYPE}" */
+	{
+		readToken (id);
+		readToken (token);
+	}
+	if (isKeyword (id, KEYWORD_like))
+	{
+		if (isType (token, TOKEN_IDENTIFIER) ||
+				isKeyword (token, KEYWORD_Current))
+			readToken (token);
+	}
+	else
+	{
+		if (isKeyword (id, KEYWORD_expanded))
+		{
+			copyToken (id, token);
+			readToken (token);
+		}
+		if (isType (id, TOKEN_IDENTIFIER))
+		{
+#ifdef TYPE_REFERENCE_TOOL
+			reportType (id);
+#endif
+			if (isType (token, TOKEN_OPEN_BRACKET))
+				parseGeneric (token, FALSE);
+			else if ((strcmp ("BIT", vStringValue (id->string)) == 0))
+				readToken (token);  /* read token after number of bits */
+		}
+	}
+	deleteToken (id);
+	return TRUE;
+}
+
+static void parseEntityType (tokenInfo *const token)
+{
+	Assert (isType (token, TOKEN_COLON));
+	readToken (token);
+
+	if (isType (token, TOKEN_BANG) || isType (token, TOKEN_QUESTION))
+		readToken (token);  /* skip over '!' or '?' */
+	parseType (token);
+}
+
+static void parseLocal (tokenInfo *const token)
+{
+	Assert (isKeyword (token, KEYWORD_local));
+	readToken (token);
+
+	/*  Check keyword first in case local clause is empty
+	 */
+	while (! isKeyword (token, KEYWORD_do)  &&
+		   ! isKeyword (token, KEYWORD_once))
+	{
+#ifndef TYPE_REFERENCE_TOOL
+		if (isType (token, TOKEN_IDENTIFIER))
+			makeEiffelLocalTag (token);
+#endif
+		readToken (token);
+		if (isType (token, TOKEN_COLON))
+			parseEntityType (token);
+	}
+}
+
+static void findFeatureEnd (tokenInfo *const token)
+{
+	boolean isFound = isKeyword (token, KEYWORD_is);
+	if (isFound)
+		readToken (token);
+	switch (token->keyword)
+	{
+		case KEYWORD_deferred:
+		case KEYWORD_do:
+		case KEYWORD_external:
+		case KEYWORD_local:
+		case KEYWORD_obsolete:
+		case KEYWORD_once:
+		case KEYWORD_require:
+		{
+			int depth = 1;
+
+			while (depth > 0)
+			{
+#ifdef TYPE_REFERENCE_TOOL
+				if (isType (token, TOKEN_OPEN_BRACE))
+				{
+					readToken (token);
+					if (isType (token, TOKEN_IDENTIFIER))
+						parseType (token);
+				}
+				else if (isType (token, TOKEN_BANG))
+				{
+					readToken (token);
+					if (isType (token, TOKEN_IDENTIFIER))
+						parseType (token);
+					if (isType (token, TOKEN_BANG))
+						readToken (token);
+				}
+				else
+#endif
+				switch (token->keyword)
+				{
+					case KEYWORD_check:
+					case KEYWORD_debug:
+					case KEYWORD_from:
+					case KEYWORD_if:
+					case KEYWORD_inspect:
+						++depth;
+						break;
+
+					case KEYWORD_local:
+						parseLocal (token);
+						break;
+
+					case KEYWORD_end:
+						--depth;
+						break;
+
+					default:
+						break;
+				}
+				readToken (token);
+			}
+			break;
+		}
+
+		default:
+			/* is this a manifest constant? */
+			if (isFound || isType (token, TOKEN_OPERATOR)) {
+				if (isType (token, TOKEN_OPERATOR))
+					readToken (token);
+				readToken (token);
+			}
+			break;
+	}
+}
+
+static boolean readFeatureName (tokenInfo *const token)
+{
+	boolean isFeatureName = FALSE;
+
+	if (isKeyword (token, KEYWORD_frozen))
+		readToken (token);
+	if (isType (token, TOKEN_IDENTIFIER))
+		isFeatureName = TRUE;
+	else if (isKeyword (token, KEYWORD_assign))  /* legacy code */
+		isFeatureName = TRUE;
+	else if (isKeyword (token, KEYWORD_infix)  ||
+			isKeyword (token, KEYWORD_prefix))
+	{
+		readToken (token);
+		if (isType (token, TOKEN_STRING))
+			isFeatureName = TRUE;
+	}
+	return isFeatureName;
+}
+
+static void parseArguments (tokenInfo *const token)
+{
+#ifndef TYPE_REFERENCE_TOOL
+	findToken (token, TOKEN_CLOSE_PAREN);
+	readToken (token);
+#else
+	Assert (isType (token, TOKEN_OPEN_PAREN));
+	readToken (token);
+	do
+	{
+		if (isType (token, TOKEN_COLON))
+			parseEntityType (token);
+		else
+			readToken (token);
+	} while (! isType (token, TOKEN_CLOSE_PAREN));
+	readToken (token);
+#endif
+}
+
+static boolean parseFeature (tokenInfo *const token)
+{
+	boolean found = FALSE;
+	while (readFeatureName (token))
+	{
+		found = TRUE;
+#ifndef TYPE_REFERENCE_TOOL
+		makeEiffelFeatureTag (token);
+#endif
+		readToken (token);
+		if (isType (token, TOKEN_COMMA))
+			readToken (token);
+	}
+	if (found)
+	{
+		if (isKeyword (token, KEYWORD_alias)) {
+			readToken (token);
+#ifndef TYPE_REFERENCE_TOOL
+			if (isType (token, TOKEN_STRING))
+				makeEiffelFeatureTag (token);
+#endif
+			readToken (token);
+		}
+		if (isType (token, TOKEN_OPEN_PAREN))  /* arguments? */
+			parseArguments (token);
+		if (isType (token, TOKEN_COLON))       /* a query? */
+			parseEntityType (token);
+		if (isKeyword (token, KEYWORD_assign))
+		{
+			readToken (token);
+			readToken (token);
+		}
+		if (isKeyword (token, KEYWORD_obsolete))
+		{
+			readToken (token);
+			if (isType (token, TOKEN_STRING))
+				readToken (token);
+		}
+		findFeatureEnd (token);
+	}
+	return found;
+}
+
+static void parseExport (tokenInfo *const token)
+{
+	token->isExported = TRUE;
+	readToken (token);
+	if (isType (token, TOKEN_OPEN_BRACE))
+	{
+		token->isExported = FALSE;
+		while (! isType (token, TOKEN_CLOSE_BRACE))
+		{
+			if (isType (token, TOKEN_IDENTIFIER))
+				token->isExported |= !isIdentifierMatch (token, "NONE");
+			readToken (token);
+		}
+		readToken (token);
+	}
+}
+
+static void parseFeatureClauses (tokenInfo *const token)
+{
+	Assert (isKeyword (token, KEYWORD_feature));
+	do
+	{
+		if (isKeyword (token, KEYWORD_feature))
+			parseExport (token);
+		if (! isKeyword (token, KEYWORD_feature) &&
+			! isKeyword (token, KEYWORD_invariant) &&
+			! isKeyword (token, KEYWORD_indexing))
+		{
+			if (! parseFeature (token))
+				readToken (token);
+		}
+	} while (! isKeyword (token, KEYWORD_end) &&
+			 ! isKeyword (token, KEYWORD_invariant) &&
+			 ! isKeyword (token, KEYWORD_indexing));
+}
+
+static void parseRename (tokenInfo *const token)
+{
+	Assert (isKeyword (token, KEYWORD_rename));
+	do {
+		readToken (token);
+		if (readFeatureName (token))
+		{
+			readToken (token);
+			if (isKeyword (token, KEYWORD_as))
+			{
+				readToken (token);
+				if (readFeatureName (token))
+				{
+#ifndef TYPE_REFERENCE_TOOL
+					makeEiffelFeatureTag (token);  /* renamed feature */
+#endif
+					readToken (token);
+				}
+			}
+		}
+	} while (isType (token, TOKEN_COMMA));
+}
+
+static void parseInherit (tokenInfo *const token)
+{
+	Assert (isKeyword (token, KEYWORD_inherit));
+	readToken (token);
+	while (isType (token, TOKEN_IDENTIFIER))
+	{
+		parseType (token);
+		if (isType (token, TOKEN_KEYWORD))
+		{
+			switch (token->keyword)  /* check for feature adaptation */
+			{
+				case KEYWORD_rename:
+					parseRename (token);
+				case KEYWORD_export:
+				case KEYWORD_undefine:
+				case KEYWORD_redefine:
+				case KEYWORD_select:
+					findKeyword (token, KEYWORD_end);
+					readToken (token);
+					break;
+
+				case KEYWORD_end:
+					readToken (token);
+					break;
+
+				default: break;
+			}
+		}
+		if (isType (token, TOKEN_SEMICOLON))
+			readToken (token);
+	}
+}
+
+static void parseConvert (tokenInfo *const token)
+{
+	Assert (isKeyword (token, KEYWORD_convert));
+	do
+	{
+		readToken (token);
+		if (! isType (token, TOKEN_IDENTIFIER))
+			break;
+		else if (isType (token, TOKEN_OPEN_PAREN))
+		{
+			while (! isType (token, TOKEN_CLOSE_PAREN))
+				readToken (token);
+		}
+		else if (isType (token, TOKEN_COLON))
+		{
+			readToken (token);
+			if (! isType (token, TOKEN_OPEN_BRACE))
+				break;
+			else while (! isType (token, TOKEN_CLOSE_BRACE))
+				readToken (token);
+		}
+	} while (isType (token, TOKEN_COMMA));
+}
+
+static void parseClass (tokenInfo *const token)
+{
+	Assert (isKeyword (token, KEYWORD_class));
+	readToken (token);
+	if (isType (token, TOKEN_IDENTIFIER))
+	{
+#ifndef TYPE_REFERENCE_TOOL
+		makeEiffelClassTag (token);
+		readToken (token);
+#else
+		vStringCopy (token->className, token->string);
+		vStringUpper (token->className);
+		if (PrintClass)
+			puts (vStringValue (token->className));
+		if (! PrintReferences)
+			exit (0);
+		readToken (token);
+#endif
+	}
+
+	do
+	{
+		if (isType (token, TOKEN_OPEN_BRACKET))
+			parseGeneric (token, TRUE);
+		else if (! isType (token, TOKEN_KEYWORD))
+			readToken (token);
+		else switch (token->keyword)
+		{
+			case KEYWORD_inherit:  parseInherit (token);        break;
+			case KEYWORD_feature:  parseFeatureClauses (token); break;
+			case KEYWORD_convert:  parseConvert (token);        break;
+			default:               readToken (token);           break;
+		}
+	} while (! isKeyword (token, KEYWORD_end));
+}
+
+static void initialize (const langType language)
+{
+	Lang_eiffel = language;
+	buildEiffelKeywordHash ();
+}
+
+static void findEiffelTags (void)
+{
+	tokenInfo *const token = newToken ();
+	exception_t exception;
+
+	exception = (exception_t) (setjmp (Exception));
+	while (exception == ExceptionNone)
+	{
+		findKeyword (token, KEYWORD_class);
+		parseClass (token);
+	}
+	deleteToken (token);
+}
+
+#ifndef TYPE_REFERENCE_TOOL
+
+extern parserDefinition* EiffelParser (void)
+{
+	static const char *const extensions [] = { "e", NULL };
+	parserDefinition* def = parserNew ("Eiffel");
+	def->kinds      = EiffelKinds;
+	def->kindCount  = KIND_COUNT (EiffelKinds);
+	def->extensions = extensions;
+	def->parser     = findEiffelTags;
+	def->initialize = initialize;
+	return def;
+}
+
+#else
+
+static void findReferences (void)
+{
+	ReferencedTypes = stringListNew ();
+	GenericNames = stringListNew ();
+	initialize (0);
+
+	findEiffelTags ();
+
+	stringListDelete (GenericNames);
+	GenericNames = NULL;
+	stringListDelete (ReferencedTypes);
+	ReferencedTypes = NULL;
+}
+
+static const char *const Usage =
+	"Prints names of types referenced by an Eiffel language file.\n"
+	"\n"
+	"Usage: %s [-cdrs] [file_name | -]\n"
+	"\n"
+	"Options:\n"
+	"    -c    Print class name of current file (on first line of output).\n"
+	"    -d    Enable debug output.\n"
+	"    -r    Print types referenced by current file (default unless -c).\n"
+	"    -s    Include self-references.\n"
+	"\n";
+
+extern int main (int argc, char** argv)
+{
+	int i;
+	for (i = 1  ;  argv [i] != NULL  ;  ++i)
+	{
+		const char *const arg = argv [i];
+		if (arg [0] == '-')
+		{
+			int j;
+			if (arg [1] == '\0')
+			{
+					File = stdin;
+					FileName = "stdin";
+			}
+			else for (j = 1  ;  arg [j] != '\0'  ;  ++j) switch (arg [j])
+			{
+				case 'c':  PrintClass      = 1;  break;
+				case 'r':  PrintReferences = 1;  break;
+				case 's':  SelfReferences  = 1;  break;
+				case 'd':  Debug           = 1;  break;
+				default:
+					fprintf (errout, "%s: unknown option: %c\n", argv [0], arg [1]);
+					fprintf (errout, Usage, argv [0]);
+					exit (1);
+					break;
+			}
+		}
+		else if (File != NULL)
+		{
+			fprintf (errout, Usage, argv [0]);
+			exit (1);
+		}
+		else
+		{
+			FileName = arg;
+			File = fopen (FileName, "r");
+			if (File == NULL)
+			{
+				perror (argv [0]);
+				exit (1);
+			}
+		}
+	}
+	if (! PrintClass)
+		PrintReferences = 1;
+	if (File == NULL)
+	{
+		fprintf (errout, Usage, argv [0]);
+		exit (1);
+	}
+	else
+	{
+		findReferences ();
+		fclose (File);
+	}
+	return 0;
+}
+
+#endif
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/general.h
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/general.h	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/general.h	(revision 5)
@@ -0,0 +1,125 @@
+/*
+*   $Id: general.h 508 2007-05-03 03:20:59Z dhiebert $
+*
+*   Copyright (c) 1998-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   Provides the general (non-ctags-specific) environment assumed by all.
+*/
+#ifndef _GENERAL_H
+#define _GENERAL_H
+
+/*
+*   INCLUDE FILES
+*/
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#elif defined (AMIGA)
+# include "e_amiga.h"
+#elif defined (DJGPP)
+# include "e_djgpp.h"
+#elif defined (macintosh)
+# include "e_mac.h"
+#elif defined (MSDOS) || defined (WIN32)
+# include "e_msoft.h"
+#elif defined (OS2)
+# include "e_os2.h"
+#elif defined (QDOS)
+# include "e_qdos.h"
+#elif defined (RISCOS)
+# include "e_riscos.h"
+#elif defined (__vms) || defined (VMS)
+# include "e_vms.h"
+# ifndef VMS
+#  define VMS 1
+# endif
+#endif
+
+
+/*
+*   MACROS
+*/
+
+/* Define standard error destination
+ */
+#ifndef errout
+# define errout	stderr
+#endif
+
+/* Define regex if supported */
+#if (defined (HAVE_REGCOMP) && !defined (REGCOMP_BROKEN))
+# define HAVE_REGEX 1
+#endif
+
+/*  This is a helpful internal feature of later versions (> 2.7) of GCC
+ *  to prevent warnings about unused variables.
+ */
+#if (__GNUC__ > 2  ||  (__GNUC__ == 2  &&  __GNUC_MINOR__ >= 7)) && !defined (__GNUG__)
+# define __printf__(s,f)  __attribute__((format (printf, s, f)))
+#else
+# define __printf__(s,f)
+#endif
+
+/*
+ *  Portability macros
+ */
+#if !defined(HAVE_STRCASECMP) && !defined(strcasecmp)
+# ifdef HAVE_STRICMP
+#  define strcasecmp(s1,s2) stricmp(s1,s2)
+# else
+#  define strcasecmp(s1,s2) struppercmp(s1,s2)
+# endif
+#endif
+
+#if !defined(HAVE_STRNCASECMP) && !defined(strncasecmp)
+# ifdef HAVE_STRNICMP
+#  define strncasecmp(s1,s2,n) strnicmp(s1,s2,n)
+# else
+#  define strncasecmp(s1,s2,n) strnuppercmp(s1,s2,n)
+# endif
+#endif
+
+/*
+*   DATA DECLARATIONS
+*/
+
+#undef FALSE
+#undef TRUE
+#ifdef VAXC
+typedef enum { FALSE, TRUE } booleanType;
+typedef int boolean;
+#else
+# ifdef __cplusplus
+typedef bool boolean;
+#define FALSE false
+#define TRUE true
+# else
+typedef enum { FALSE, TRUE } boolean;
+# endif
+#endif
+
+#if ! defined (HAVE_FGETPOS) && ! defined (fpos_t)
+# define fpos_t long
+#endif
+
+/*
+*   FUNCTION PROTOTYPES
+*/
+
+#if defined (NEED_PROTO_REMOVE) && defined (HAVE_REMOVE)
+extern int remove (const char *);
+#endif
+
+#if defined (NEED_PROTO_UNLINK) && ! defined (HAVE_REMOVE)
+extern void *unlink (const char *);
+#endif
+
+#ifdef NEED_PROTO_GETENV
+extern char *getenv (const char *);
+#endif
+
+#endif  /* _GENERAL_H */
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/lregex.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/lregex.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/lregex.c	(revision 5)
@@ -0,0 +1,704 @@
+/*
+*   $Id: lregex.c 576 2007-06-30 04:16:23Z elliotth $
+*
+*   Copyright (c) 2000-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions for applying regular expression matching.
+*
+*   The code for utlizing the Gnu regex package with regards to processing the
+*   regex option and checking for regex matches was adapted from routines in
+*   Gnu etags.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include <string.h>
+
+#ifdef HAVE_REGCOMP
+# include <ctype.h>
+# include <stddef.h>
+# ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>  /* declare off_t (not known to regex.h on FreeBSD) */
+# endif
+# include <regex.h>
+#endif
+
+#include "debug.h"
+#include "entry.h"
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+
+#ifdef HAVE_REGEX
+
+/*
+*   MACROS
+*/
+
+/* Back-references \0 through \9 */
+#define BACK_REFERENCE_COUNT 10
+
+#if defined (HAVE_REGCOMP) && !defined (REGCOMP_BROKEN)
+# define POSIX_REGEX
+#endif
+
+#define REGEX_NAME "Regex"
+
+/*
+*   DATA DECLARATIONS
+*/
+#if defined (POSIX_REGEX)
+
+struct sKind {
+	boolean enabled;
+	char letter;
+	char* name;
+	char* description;
+};
+
+enum pType { PTRN_TAG, PTRN_CALLBACK };
+
+typedef struct {
+	regex_t *pattern;
+	enum pType type;
+	union {
+		struct {
+			char *name_pattern;
+			struct sKind kind;
+		} tag;
+		struct {
+			regexCallback function;
+		} callback;
+	} u;
+} regexPattern;
+
+#endif
+
+typedef struct {
+	regexPattern *patterns;
+	unsigned int count;
+} patternSet;
+
+/*
+*   DATA DEFINITIONS
+*/
+
+static boolean regexBroken = FALSE;
+
+/* Array of pattern sets, indexed by language */
+static patternSet* Sets = NULL;
+static int SetUpper = -1;  /* upper language index in list */
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+static void clearPatternSet (const langType language)
+{
+	if (language <= SetUpper)
+	{
+		patternSet* const set = Sets + language;
+		unsigned int i;
+		for (i = 0  ;  i < set->count  ;  ++i)
+		{
+			regexPattern *p = &set->patterns [i];
+#if defined (POSIX_REGEX)
+			regfree (p->pattern);
+#endif
+			eFree (p->pattern);
+			p->pattern = NULL;
+
+			if (p->type == PTRN_TAG)
+			{
+				eFree (p->u.tag.name_pattern);
+				p->u.tag.name_pattern = NULL;
+				eFree (p->u.tag.kind.name);
+				p->u.tag.kind.name = NULL;
+				if (p->u.tag.kind.description != NULL)
+				{
+					eFree (p->u.tag.kind.description);
+					p->u.tag.kind.description = NULL;
+				}
+			}
+		}
+		if (set->patterns != NULL)
+			eFree (set->patterns);
+		set->patterns = NULL;
+		set->count = 0;
+	}
+}
+
+/*
+*   Regex psuedo-parser
+*/
+
+static void makeRegexTag (
+		const vString* const name, const struct sKind* const kind)
+{
+	if (kind->enabled)
+	{
+		tagEntryInfo e;
+		Assert (name != NULL  &&  vStringLength (name) > 0);
+		Assert (kind != NULL);
+		initTagEntry (&e, vStringValue (name));
+		e.kind     = kind->letter;
+		e.kindName = kind->name;
+		makeTagEntry (&e);
+	}
+}
+
+/*
+*   Regex pattern definition
+*/
+
+/* Take a string like "/blah/" and turn it into "blah", making sure
+ * that the first and last characters are the same, and handling
+ * quoted separator characters.  Actually, stops on the occurrence of
+ * an unquoted separator.  Also turns "\t" into a Tab character.
+ * Returns pointer to terminating separator.  Works in place.  Null
+ * terminates name string.
+ */
+static char* scanSeparators (char* name)
+{
+	char sep = name [0];
+	char *copyto = name;
+	boolean quoted = FALSE;
+
+	for (++name ; *name != '\0' ; ++name)
+	{
+		if (quoted)
+		{
+			if (*name == sep)
+				*copyto++ = sep;
+			else if (*name == 't')
+				*copyto++ = '\t';
+			else
+			{
+				/* Something else is quoted, so preserve the quote. */
+				*copyto++ = '\\';
+				*copyto++ = *name;
+			}
+			quoted = FALSE;
+		}
+		else if (*name == '\\')
+			quoted = TRUE;
+		else if (*name == sep)
+		{
+			break;
+		}
+		else
+			*copyto++ = *name;
+	}
+	*copyto = '\0';
+	return name;
+}
+
+/* Parse `regexp', in form "/regex/name/[k,Kind/]flags" (where the separator
+ * character is whatever the first character of `regexp' is), by breaking it
+ * up into null terminated strings, removing the separators, and expanding
+ * '\t' into tabs. When complete, `regexp' points to the line matching
+ * pattern, a pointer to the name matching pattern is written to `name', a
+ * pointer to the kinds is written to `kinds' (possibly NULL), and a pointer
+ * to the trailing flags is written to `flags'. If the pattern is not in the
+ * correct format, a false value is returned.
+ */
+static boolean parseTagRegex (
+		char* const regexp, char** const name,
+		char** const kinds, char** const flags)
+{
+	boolean result = FALSE;
+	const int separator = (unsigned char) regexp [0];
+
+	*name = scanSeparators (regexp);
+	if (*regexp == '\0')
+		error (WARNING, "empty regexp");
+	else if (**name != separator)
+		error (WARNING, "%s: incomplete regexp", regexp);
+	else
+	{
+		char* const third = scanSeparators (*name);
+		if (**name == '\0')
+			error (WARNING, "%s: regexp missing name pattern", regexp);
+		if ((*name) [strlen (*name) - 1] == '\\')
+			error (WARNING, "error in name pattern: \"%s\"", *name);
+		if (*third != separator)
+			error (WARNING, "%s: regexp missing final separator", regexp);
+		else
+		{
+			char* const fourth = scanSeparators (third);
+			if (*fourth == separator)
+			{
+				*kinds = third;
+				scanSeparators (fourth);
+				*flags = fourth;
+			}
+			else
+			{
+				*flags = third;
+				*kinds = NULL;
+			}
+			result = TRUE;
+		}
+	}
+	return result;
+}
+
+static void addCompiledTagPattern (
+		const langType language, regex_t* const pattern,
+		char* const name, const char kind, char* const kindName,
+		char *const description)
+{
+	patternSet* set;
+	regexPattern *ptrn;
+	if (language > SetUpper)
+	{
+		int i;
+		Sets = xRealloc (Sets, (language + 1), patternSet);
+		for (i = SetUpper + 1  ;  i <= language  ;  ++i)
+		{
+			Sets [i].patterns = NULL;
+			Sets [i].count = 0;
+		}
+		SetUpper = language;
+	}
+	set = Sets + language;
+	set->patterns = xRealloc (set->patterns, (set->count + 1), regexPattern);
+	ptrn = &set->patterns [set->count];
+	set->count += 1;
+
+	ptrn->pattern = pattern;
+	ptrn->type    = PTRN_TAG;
+	ptrn->u.tag.name_pattern = name;
+	ptrn->u.tag.kind.enabled = TRUE;
+	ptrn->u.tag.kind.letter  = kind;
+	ptrn->u.tag.kind.name    = kindName;
+	ptrn->u.tag.kind.description = description;
+}
+
+static void addCompiledCallbackPattern (
+		const langType language, regex_t* const pattern,
+		const regexCallback callback)
+{
+	patternSet* set;
+	regexPattern *ptrn;
+	if (language > SetUpper)
+	{
+		int i;
+		Sets = xRealloc (Sets, (language + 1), patternSet);
+		for (i = SetUpper + 1  ;  i <= language  ;  ++i)
+		{
+			Sets [i].patterns = NULL;
+			Sets [i].count = 0;
+		}
+		SetUpper = language;
+	}
+	set = Sets + language;
+	set->patterns = xRealloc (set->patterns, (set->count + 1), regexPattern);
+	ptrn = &set->patterns [set->count];
+	set->count += 1;
+
+	ptrn->pattern = pattern;
+	ptrn->type    = PTRN_CALLBACK;
+	ptrn->u.callback.function = callback;
+}
+
+#if defined (POSIX_REGEX)
+
+static regex_t* compileRegex (const char* const regexp, const char* const flags)
+{
+	int cflags = REG_EXTENDED | REG_NEWLINE;
+	regex_t *result = NULL;
+	int errcode;
+	int i;
+	for (i = 0  ; flags != NULL  &&  flags [i] != '\0'  ;  ++i)
+	{
+		switch ((int) flags [i])
+		{
+			case 'b': cflags &= ~REG_EXTENDED; break;
+			case 'e': cflags |= REG_EXTENDED;  break;
+			case 'i': cflags |= REG_ICASE;     break;
+			default: error (WARNING, "unknown regex flag: '%c'", *flags); break;
+		}
+	}
+	result = xMalloc (1, regex_t);
+	errcode = regcomp (result, regexp, cflags);
+	if (errcode != 0)
+	{
+		char errmsg[256];
+		regerror (errcode, result, errmsg, 256);
+		error (WARNING, "regcomp %s: %s", regexp, errmsg);
+		regfree (result);
+		eFree (result);
+		result = NULL;
+	}
+	return result;
+}
+
+#endif
+
+static void parseKinds (
+		const char* const kinds, char* const kind, char** const kindName,
+		char **description)
+{
+	*kind = '\0';
+	*kindName = NULL;
+	*description = NULL;
+	if (kinds == NULL  ||  kinds [0] == '\0')
+	{
+		*kind = 'r';
+		*kindName = eStrdup ("regex");
+	}
+	else if (kinds [0] != '\0')
+	{
+		const char* k = kinds;
+		if (k [0] != ','  &&  (k [1] == ','  ||  k [1] == '\0'))
+			*kind = *k++;
+		else
+			*kind = 'r';
+		if (*k == ',')
+			++k;
+		if (k [0] == '\0')
+			*kindName = eStrdup ("regex");
+		else
+		{
+			const char *const comma = strchr (k, ',');
+			if (comma == NULL)
+				*kindName = eStrdup (k);
+			else
+			{
+				*kindName = (char*) eMalloc (comma - k + 1);
+				strncpy (*kindName, k, comma - k);
+				(*kindName) [comma - k] = '\0';
+				k = comma + 1;
+				if (k [0] != '\0')
+					*description = eStrdup (k);
+			}
+		}
+	}
+}
+
+static void printRegexKind (const regexPattern *pat, unsigned int i, boolean indent)
+{
+	const struct sKind *const kind = &pat [i].u.tag.kind;
+	const char *const indentation = indent ? "    " : "";
+	Assert (pat [i].type == PTRN_TAG);
+	printf ("%s%c  %s %s\n", indentation,
+			kind->letter != '\0' ? kind->letter : '?',
+			kind->description != NULL ? kind->description : kind->name,
+			kind->enabled ? "" : " [off]");
+}
+
+static void processLanguageRegex (const langType language,
+		const char* const parameter)
+{
+	if (parameter == NULL  ||  parameter [0] == '\0')
+		clearPatternSet (language);
+	else if (parameter [0] != '@')
+		addLanguageRegex (language, parameter);
+	else if (! doesFileExist (parameter + 1))
+		error (WARNING, "cannot open regex file");
+	else
+	{
+		const char* regexfile = parameter + 1;
+		FILE* const fp = fopen (regexfile, "r");
+		if (fp == NULL)
+			error (WARNING | PERROR, regexfile);
+		else
+		{
+			vString* const regex = vStringNew ();
+			while (readLine (regex, fp))
+				addLanguageRegex (language, vStringValue (regex));
+			fclose (fp);
+			vStringDelete (regex);
+		}
+	}
+}
+
+/*
+*   Regex pattern matching
+*/
+
+#if defined (POSIX_REGEX)
+
+static vString* substitute (
+		const char* const in, const char* out,
+		const int nmatch, const regmatch_t* const pmatch)
+{
+	vString* result = vStringNew ();
+	const char* p;
+	for (p = out  ;  *p != '\0'  ;  p++)
+	{
+		if (*p == '\\'  &&  isdigit ((int) *++p))
+		{
+			const int dig = *p - '0';
+			if (0 < dig  &&  dig < nmatch  &&  pmatch [dig].rm_so != -1)
+			{
+				const int diglen = pmatch [dig].rm_eo - pmatch [dig].rm_so;
+				vStringNCatS (result, in + pmatch [dig].rm_so, diglen);
+			}
+		}
+		else if (*p != '\n'  &&  *p != '\r')
+			vStringPut (result, *p);
+	}
+	vStringTerminate (result);
+	return result;
+}
+
+static void matchTagPattern (const vString* const line,
+		const regexPattern* const patbuf,
+		const regmatch_t* const pmatch)
+{
+	vString *const name = substitute (vStringValue (line),
+			patbuf->u.tag.name_pattern, BACK_REFERENCE_COUNT, pmatch);
+	vStringStripLeading (name);
+	vStringStripTrailing (name);
+	if (vStringLength (name) > 0)
+		makeRegexTag (name, &patbuf->u.tag.kind);
+	else
+		error (WARNING, "%s:%ld: null expansion of name pattern \"%s\"",
+			getInputFileName (), getInputLineNumber (),
+			patbuf->u.tag.name_pattern);
+	vStringDelete (name);
+}
+
+static void matchCallbackPattern (
+		const vString* const line, const regexPattern* const patbuf,
+		const regmatch_t* const pmatch)
+{
+	regexMatch matches [BACK_REFERENCE_COUNT];
+	unsigned int count = 0;
+	int i;
+	for (i = 0  ;  i < BACK_REFERENCE_COUNT  &&  pmatch [i].rm_so != -1  ;  ++i)
+	{
+		matches [i].start  = pmatch [i].rm_so;
+		matches [i].length = pmatch [i].rm_eo - pmatch [i].rm_so;
+		++count;
+	}
+	patbuf->u.callback.function (vStringValue (line), matches, count);
+}
+
+static boolean matchRegexPattern (const vString* const line,
+		const regexPattern* const patbuf)
+{
+	boolean result = FALSE;
+	regmatch_t pmatch [BACK_REFERENCE_COUNT];
+	const int match = regexec (patbuf->pattern, vStringValue (line),
+							   BACK_REFERENCE_COUNT, pmatch, 0);
+	if (match == 0)
+	{
+		result = TRUE;
+		if (patbuf->type == PTRN_TAG)
+			matchTagPattern (line, patbuf, pmatch);
+		else if (patbuf->type == PTRN_CALLBACK)
+			matchCallbackPattern (line, patbuf, pmatch);
+		else
+		{
+			Assert ("invalid pattern type" == NULL);
+			result = FALSE;
+		}
+	}
+	return result;
+}
+
+#endif
+
+/* PUBLIC INTERFACE */
+
+/* Match against all patterns for specified language. Returns true if at least
+ * on pattern matched.
+ */
+extern boolean matchRegex (const vString* const line, const langType language)
+{
+	boolean result = FALSE;
+	if (language != LANG_IGNORE  &&  language <= SetUpper  &&
+		Sets [language].count > 0)
+	{
+		const patternSet* const set = Sets + language;
+		unsigned int i;
+		for (i = 0  ;  i < set->count  ;  ++i)
+			if (matchRegexPattern (line, set->patterns + i))
+				result = TRUE;
+	}
+	return result;
+}
+
+extern void findRegexTags (void)
+{
+	/* merely read all lines of the file */
+	while (fileReadLine () != NULL)
+		;
+}
+
+#endif  /* HAVE_REGEX */
+
+extern void addTagRegex (
+		const langType language,
+		const char* const regex,
+		const char* const name,
+		const char* const kinds,
+		const char* const flags)
+{
+#ifdef HAVE_REGEX
+	Assert (regex != NULL);
+	Assert (name != NULL);
+	if (! regexBroken)
+	{
+		regex_t* const cp = compileRegex (regex, flags);
+		if (cp != NULL)
+		{
+			char kind;
+			char* kindName;
+			char* description;
+			parseKinds (kinds, &kind, &kindName, &description);
+			addCompiledTagPattern (language, cp, eStrdup (name),
+					kind, kindName, description);
+		}
+	}
+#endif
+}
+
+extern void addCallbackRegex (
+		const langType language,
+		const char* const regex,
+		const char* const flags,
+		const regexCallback callback)
+{
+#ifdef HAVE_REGEX
+	Assert (regex != NULL);
+	if (! regexBroken)
+	{
+		regex_t* const cp = compileRegex (regex, flags);
+		if (cp != NULL)
+			addCompiledCallbackPattern (language, cp, callback);
+	}
+#endif
+}
+
+extern void addLanguageRegex (
+		const langType language, const char* const regex)
+{
+#ifdef HAVE_REGEX
+	if (! regexBroken)
+	{
+		char *const regex_pat = eStrdup (regex);
+		char *name, *kinds, *flags;
+		if (parseTagRegex (regex_pat, &name, &kinds, &flags))
+		{
+			addTagRegex (language, regex_pat, name, kinds, flags);
+			eFree (regex_pat);
+		}
+	}
+#endif
+}
+
+/*
+*   Regex option parsing
+*/
+
+extern boolean processRegexOption (const char *const option,
+								   const char *const parameter)
+{
+	boolean handled = FALSE;
+	const char* const dash = strchr (option, '-');
+	if (dash != NULL  &&  strncmp (option, "regex", dash - option) == 0)
+	{
+#ifdef HAVE_REGEX
+		langType language;
+		language = getNamedLanguage (dash + 1);
+		if (language == LANG_IGNORE)
+			error (WARNING, "unknown language \"%s\" in --%s option", (dash + 1), option);
+		else
+			processLanguageRegex (language, parameter);
+#else
+		error (WARNING, "regex support not available; required for --%s option",
+		   option);
+#endif
+		handled = TRUE;
+	}
+	return handled;
+}
+
+extern void disableRegexKinds (const langType language)
+{
+#ifdef HAVE_REGEX
+	if (language <= SetUpper  &&  Sets [language].count > 0)
+	{
+		patternSet* const set = Sets + language;
+		unsigned int i;
+		for (i = 0  ;  i < set->count  ;  ++i)
+			if (set->patterns [i].type == PTRN_TAG)
+				set->patterns [i].u.tag.kind.enabled = FALSE;
+	}
+#endif
+}
+
+extern boolean enableRegexKind (
+		const langType language,
+		const int kind, const boolean mode)
+{
+	boolean result = FALSE;
+#ifdef HAVE_REGEX
+	if (language <= SetUpper  &&  Sets [language].count > 0)
+	{
+		patternSet* const set = Sets + language;
+		unsigned int i;
+		for (i = 0  ;  i < set->count  ;  ++i)
+			if (set->patterns [i].type == PTRN_TAG &&
+				set->patterns [i].u.tag.kind.letter == kind)
+			{
+				set->patterns [i].u.tag.kind.enabled = mode;
+				result = TRUE;
+			}
+	}
+#endif
+	return result;
+}
+
+extern void printRegexKinds (const langType language, boolean indent)
+{
+#ifdef HAVE_REGEX
+	if (language <= SetUpper  &&  Sets [language].count > 0)
+	{
+		patternSet* const set = Sets + language;
+		unsigned int i;
+		for (i = 0  ;  i < set->count  ;  ++i)
+			if (set->patterns [i].type == PTRN_TAG)
+				printRegexKind (set->patterns, i, indent);
+	}
+#endif
+}
+
+extern void freeRegexResources (void)
+{
+#ifdef HAVE_REGEX
+	int i;
+	for (i = 0  ;  i <= SetUpper  ;  ++i)
+		clearPatternSet (i);
+	if (Sets != NULL)
+		eFree (Sets);
+	Sets = NULL;
+	SetUpper = -1;
+#endif
+}
+
+/* Check for broken regcomp() on Cygwin */
+extern void checkRegex (void)
+{
+#if defined (HAVE_REGEX) && defined (CHECK_REGCOMP)
+	regex_t patbuf;
+	int errcode;
+	if (regcomp (&patbuf, "/hello/", 0) != 0)
+	{
+		error (WARNING, "Disabling broken regex");
+		regexBroken = TRUE;
+	}
+#endif
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/lua.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/lua.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/lua.c	(revision 5)
@@ -0,0 +1,133 @@
+/*
+*   $Id: lua.c 443 2006-05-30 04:37:13Z darren $
+*
+*   Copyright (c) 2000-2001, Max Ischenko <mfi@ukr.net>.
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions for generating tags for Lua language.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include <string.h>
+
+#include "options.h"
+#include "parse.h"
+#include "read.h"
+#include "vstring.h"
+
+/*
+*   DATA DEFINITIONS
+*/
+typedef enum {
+	K_FUNCTION
+} luaKind;
+
+static kindOption LuaKinds [] = {
+	{ TRUE, 'f', "function", "functions" }
+};
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+/* for debugging purposes */
+static void print_string (char *p, char *q)
+{
+	for ( ; p != q; p++)
+		fprintf (errout, "%c", *p);
+	fprintf (errout, "\n");
+}
+
+/*
+ * Helper function.
+ * Returns 1 if line looks like a line of Lua code.
+ *
+ * TODO: Recognize UNIX bang notation.
+ * (Lua treat first line as a comment if it starts with #!)
+ *
+ */
+static boolean is_a_code_line (const unsigned char *line)
+{
+	boolean result;
+	const unsigned char *p = line;
+	while (isspace ((int) *p))
+		p++;
+	if (p [0] == '\0')
+		result = FALSE;
+	else if (p [0] == '-' && p [1] == '-')
+		result = FALSE;
+	else
+		result = TRUE;
+	return result;
+}
+
+static void extract_name (const char *begin, const char *end, vString *name)
+{
+	if (begin != NULL  &&  end != NULL  &&  begin < end)
+	{
+		const char *cp;
+
+		while (isspace ((int) *begin))
+			begin++;
+		while (isspace ((int) *end))
+			end--;
+		if (begin < end)
+		{
+			for (cp = begin ; cp != end; cp++)
+				vStringPut (name, (int) *cp);
+			vStringTerminate (name);
+
+			makeSimpleTag (name, LuaKinds, K_FUNCTION);
+			vStringClear (name);
+		}
+	}
+}
+
+static void findLuaTags (void)
+{
+	vString *name = vStringNew ();
+	const unsigned char *line;
+
+	while ((line = fileReadLine ()) != NULL)
+	{
+		const char *p, *q;
+
+		if (! is_a_code_line (line))
+			continue;
+
+		p = (const char*) strstr ((const char*) line, "function");
+		if (p == NULL)
+			continue;
+
+		q = strchr ((const char*) line, '=');
+		
+		if (q == NULL) {
+			p = p + 9;  /* skip the `function' word */
+			q = strchr ((const char*) p, '(');
+			extract_name (p, q, name);
+		} else {
+			p = (const char*) &line[0];
+			extract_name (p, q, name);
+		}
+	}
+	vStringDelete (name);
+}
+
+extern parserDefinition* LuaParser (void)
+{
+	static const char* const extensions [] = { "lua", NULL };
+	parserDefinition* def = parserNew ("Lua");
+	def->kinds      = LuaKinds;
+	def->kindCount  = KIND_COUNT (LuaKinds);
+	def->extensions = extensions;
+	def->parser     = findLuaTags;
+	return def;
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/main.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/main.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/main.c	(revision 5)
@@ -0,0 +1,579 @@
+/*
+*   $Id: main.c 536 2007-06-02 06:09:00Z elliotth $
+*
+*   Copyright (c) 1996-2003, Darren Hiebert
+*
+*   Author: Darren Hiebert <dhiebert@users.sourceforge.net>
+*           http://ctags.sourceforge.net
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License. It is provided on an as-is basis and no
+*   responsibility is accepted for its failure to perform as expected.
+*
+*   This is a reimplementation of the ctags (1) program. It is an attempt to
+*   provide a fully featured ctags program which is free of the limitations
+*   which most (all?) others are subject to.
+*
+*   This module contains the start-up code and routines to determine the list
+*   of files to parsed for tags.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include <string.h>
+
+/*  To provide timings features if available.
+ */
+#ifdef HAVE_CLOCK
+# ifdef HAVE_TIME_H
+#  include <time.h>
+# endif
+#else
+# ifdef HAVE_TIMES
+#  ifdef HAVE_SYS_TIMES_H
+#   include <sys/times.h>
+#  endif
+# endif
+#endif
+
+/*  To provide directory searching for recursion feature.
+ */
+#ifdef AMIGA
+# include <dos/dosasl.h>       /* for struct AnchorPath */
+# include <clib/dos_protos.h>  /* function prototypes */
+# define ANCHOR_BUF_SIZE 512
+# define ANCHOR_SIZE (sizeof (struct AnchorPath) + ANCHOR_BUF_SIZE)
+# ifdef __SASC
+   extern struct DosLibrary *DOSBase;
+#  include <pragmas/dos_pragmas.h>
+# endif
+#endif
+
+#ifdef HAVE_DIRENT_H
+# ifdef __BORLANDC__
+#  define boolean BORLAND_boolean
+# endif
+# ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>  /* required by dirent.h */
+# endif
+# include <dirent.h>  /* to declare opendir() */
+# undef boolean
+#endif
+#ifdef HAVE_DIRECT_H
+# include <direct.h>  /* to _getcwd() */
+#endif
+#ifdef HAVE_DOS_H
+# include <dos.h>  /* to declare FA_DIREC */
+#endif
+#ifdef HAVE_DIR_H
+# include <dir.h>  /* to declare findfirst() and findnext */
+#endif
+#ifdef HAVE_IO_H
+# include <io.h>  /* to declare _findfirst() */
+#endif
+
+
+#include "debug.h"
+#include "keyword.h"
+#include "main.h"
+#include "options.h"
+#include "read.h"
+#include "routines.h"
+
+/*
+*   MACROS
+*/
+#define plural(value)  (((unsigned long)(value) == 1L) ? "" : "s")
+
+/*
+*   DATA DEFINITIONS
+*/
+static struct { long files, lines, bytes; } Totals = { 0, 0, 0 };
+
+#ifdef AMIGA
+# include "ctags.h"
+  static const char *VERsion = "$VER: "PROGRAM_NAME" "PROGRAM_VERSION" "
+# ifdef __SASC
+  __AMIGADATE__
+# else
+  __DATE__
+# endif
+  " "AUTHOR_NAME" $";
+#endif
+
+/*
+*   FUNCTION PROTOTYPES
+*/
+static boolean createTagsForEntry (const char *const entryName);
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+extern void addTotals (
+		const unsigned int files, const long unsigned int lines,
+		const long unsigned int bytes)
+{
+	Totals.files += files;
+	Totals.lines += lines;
+	Totals.bytes += bytes;
+}
+
+extern boolean isDestinationStdout (void)
+{
+	boolean toStdout = FALSE;
+
+	if (Option.xref  ||  Option.filter  ||
+		(Option.tagFileName != NULL  &&  (strcmp (Option.tagFileName, "-") == 0
+#if defined (VMS)
+	|| strcmp (Option.tagFileName, "sys$output") == 0
+#else
+	|| strcmp (Option.tagFileName, "/dev/stdout") == 0
+#endif
+		)))
+		toStdout = TRUE;
+	return toStdout;
+}
+
+#if defined (HAVE_OPENDIR)
+static boolean recurseUsingOpendir (const char *const dirName)
+{
+	boolean resize = FALSE;
+	DIR *const dir = opendir (dirName);
+	if (dir == NULL)
+		error (WARNING | PERROR, "cannot recurse into directory \"%s\"", dirName);
+	else
+	{
+		struct dirent *entry;
+		while ((entry = readdir (dir)) != NULL)
+		{
+			if (strcmp (entry->d_name, ".") != 0  &&
+				strcmp (entry->d_name, "..") != 0)
+			{
+				vString *filePath;
+				if (strcmp (dirName, ".") == 0)
+					filePath = vStringNewInit (entry->d_name);
+				else
+					filePath = combinePathAndFile (dirName, entry->d_name);
+				resize |= createTagsForEntry (vStringValue (filePath));
+				vStringDelete (filePath);
+			}
+		}
+		closedir (dir);
+	}
+	return resize;
+}
+
+#elif defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
+
+static boolean createTagsForWildcardEntry (
+		const char *const pattern, const size_t dirLength,
+		const char *const entryName)
+{
+	boolean resize = FALSE;
+	/* we must not recurse into the directories "." or ".." */
+	if (strcmp (entryName, ".") != 0  &&  strcmp (entryName, "..") != 0)
+	{
+		vString *const filePath = vStringNew ();
+		vStringNCopyS (filePath, pattern, dirLength);
+		vStringCatS (filePath, entryName);
+		resize = createTagsForEntry (vStringValue (filePath));
+		vStringDelete (filePath);
+	}
+	return resize;
+}
+
+static boolean createTagsForWildcardUsingFindfirst (const char *const pattern)
+{
+	boolean resize = FALSE;
+	const size_t dirLength = baseFilename (pattern) - pattern;
+#if defined (HAVE_FINDFIRST)
+	struct ffblk fileInfo;
+	int result = findfirst (pattern, &fileInfo, FA_DIREC);
+	while (result == 0)
+	{
+		const char *const entry = (const char *) fileInfo.ff_name;
+		resize |= createTagsForWildcardEntry (pattern, dirLength, entry);
+		result = findnext (&fileInfo);
+	}
+#elif defined (HAVE__FINDFIRST)
+	struct _finddata_t fileInfo;
+	findfirst_t hFile = _findfirst (pattern, &fileInfo);
+	if (hFile != -1L)
+	{
+		do
+		{
+			const char *const entry = (const char *) fileInfo.name;
+			resize |= createTagsForWildcardEntry (pattern, dirLength, entry);
+		} while (_findnext (hFile, &fileInfo) == 0);
+		_findclose (hFile);
+	}
+#endif
+	return resize;
+}
+
+#elif defined (AMIGA)
+
+static boolean createTagsForAmigaWildcard (const char *const pattern)
+{
+	boolean resize = FALSE;
+	struct AnchorPath *const anchor =
+			(struct AnchorPath *) eMalloc ((size_t) ANCHOR_SIZE);
+	LONG result;
+
+	memset (anchor, 0, (size_t) ANCHOR_SIZE);
+	anchor->ap_Strlen = ANCHOR_BUF_SIZE;
+	/* Allow '.' for current directory */
+#ifdef APF_DODOT
+	anchor->ap_Flags = APF_DODOT | APF_DOWILD;
+#else
+	anchor->ap_Flags = APF_DoDot | APF_DoWild;
+#endif
+	result = MatchFirst ((UBYTE *) pattern, anchor);
+	while (result == 0)
+	{
+		resize |= createTagsForEntry ((char *) anchor->ap_Buf);
+		result = MatchNext (anchor);
+	}
+	MatchEnd (anchor);
+	eFree (anchor);
+	return resize;
+}
+#endif
+
+static boolean recurseIntoDirectory (const char *const dirName)
+{
+	boolean resize = FALSE;
+	if (isRecursiveLink (dirName))
+		verbose ("ignoring \"%s\" (recursive link)\n", dirName);
+	else if (! Option.recurse)
+		verbose ("ignoring \"%s\" (directory)\n", dirName);
+	else
+	{
+		verbose ("RECURSING into directory \"%s\"\n", dirName);
+#if defined (HAVE_OPENDIR)
+		resize = recurseUsingOpendir (dirName);
+#elif defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
+		{
+			vString *const pattern = vStringNew ();
+			vStringCopyS (pattern, dirName);
+			vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
+			vStringCatS (pattern, "*.*");
+			resize = createTagsForWildcardUsingFindfirst (vStringValue (pattern));
+			vStringDelete (pattern);
+		}
+#elif defined (AMIGA)
+		{
+			vString *const pattern = vStringNew ();
+			if (*dirName != '\0'  &&  strcmp (dirName, ".") != 0)
+			{
+				vStringCopyS (pattern, dirName);
+				if (dirName [strlen (dirName) - 1] != '/')
+					vStringPut (pattern, '/');
+			}
+			vStringCatS (pattern, "#?");
+			resize = createTagsForAmigaWildcard (vStringValue (pattern));
+			vStringDelete (pattern);
+		}
+#endif
+	}
+	return resize;
+}
+
+static boolean createTagsForEntry (const char *const entryName)
+{
+	boolean resize = FALSE;
+	fileStatus *status = eStat (entryName);
+
+	Assert (entryName != NULL);
+	if (isExcludedFile (entryName))
+		verbose ("excluding \"%s\"\n", entryName);
+	else if (status->isSymbolicLink  &&  ! Option.followLinks)
+		verbose ("ignoring \"%s\" (symbolic link)\n", entryName);
+	else if (! status->exists)
+		error (WARNING | PERROR, "cannot open source file \"%s\"", entryName);
+	else if (status->isDirectory)
+		resize = recurseIntoDirectory (entryName);
+	else if (! status->isNormalFile)
+		verbose ("ignoring \"%s\" (special file)\n", entryName);
+	else
+		resize = parseFile (entryName);
+
+	eStatFree (status);
+	return resize;
+}
+
+#ifdef MANUAL_GLOBBING
+
+static boolean createTagsForWildcardArg (const char *const arg)
+{
+	boolean resize = FALSE;
+	vString *const pattern = vStringNewInit (arg);
+	char *patternS = vStringValue (pattern);
+
+#if defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
+	/*  We must transform the "." and ".." forms into something that can
+	 *  be expanded by the findfirst/_findfirst functions.
+	 */
+	if (Option.recurse  &&
+		(strcmp (patternS, ".") == 0  ||  strcmp (patternS, "..") == 0))
+	{
+		vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
+		vStringCatS (pattern, "*.*");
+	}
+	resize |= createTagsForWildcardUsingFindfirst (patternS);
+#endif
+	vStringDelete (pattern);
+	return resize;
+}
+
+#endif
+
+static boolean createTagsForArgs (cookedArgs *const args)
+{
+	boolean resize = FALSE;
+
+	/*  Generate tags for each argument on the command line.
+	 */
+	while (! cArgOff (args))
+	{
+		const char *const arg = cArgItem (args);
+
+#ifdef MANUAL_GLOBBING
+		resize |= createTagsForWildcardArg (arg);
+#else
+		resize |= createTagsForEntry (arg);
+#endif
+		cArgForth (args);
+		parseOptions (args);
+	}
+	return resize;
+}
+
+/*  Read from an opened file a list of file names for which to generate tags.
+ */
+static boolean createTagsFromFileInput (FILE *const fp, const boolean filter)
+{
+	boolean resize = FALSE;
+	if (fp != NULL)
+	{
+		cookedArgs *args = cArgNewFromLineFile (fp);
+		parseOptions (args);
+		while (! cArgOff (args))
+		{
+			resize |= createTagsForEntry (cArgItem (args));
+			if (filter)
+			{
+				if (Option.filterTerminator != NULL)
+					fputs (Option.filterTerminator, stdout);
+				fflush (stdout);
+			}
+			cArgForth (args);
+			parseOptions (args);
+		}
+		cArgDelete (args);
+	}
+	return resize;
+}
+
+/*  Read from a named file a list of file names for which to generate tags.
+ */
+static boolean createTagsFromListFile (const char *const fileName)
+{
+	boolean resize;
+	Assert (fileName != NULL);
+	if (strcmp (fileName, "-") == 0)
+		resize = createTagsFromFileInput (stdin, FALSE);
+	else
+	{
+		FILE *const fp = fopen (fileName, "r");
+		if (fp == NULL)
+			error (FATAL | PERROR, "cannot open list file \"%s\"", fileName);
+		resize = createTagsFromFileInput (fp, FALSE);
+		fclose (fp);
+	}
+	return resize;
+}
+
+#if defined (HAVE_CLOCK)
+# define CLOCK_AVAILABLE
+# ifndef CLOCKS_PER_SEC
+#  define CLOCKS_PER_SEC		1000000
+# endif
+#elif defined (HAVE_TIMES)
+# define CLOCK_AVAILABLE
+# define CLOCKS_PER_SEC	60
+static clock_t clock (void)
+{
+	struct tms buf;
+
+	times (&buf);
+	return (buf.tms_utime + buf.tms_stime);
+}
+#else
+# define clock()  (clock_t)0
+#endif
+
+static void printTotals (const clock_t *const timeStamps)
+{
+	const unsigned long totalTags = TagFile.numTags.added +
+									TagFile.numTags.prev;
+
+	fprintf (errout, "%ld file%s, %ld line%s (%ld kB) scanned",
+			Totals.files, plural (Totals.files),
+			Totals.lines, plural (Totals.lines),
+			Totals.bytes/1024L);
+#ifdef CLOCK_AVAILABLE
+	{
+		const double interval = ((double) (timeStamps [1] - timeStamps [0])) /
+								CLOCKS_PER_SEC;
+
+		fprintf (errout, " in %.01f seconds", interval);
+		if (interval != (double) 0.0)
+			fprintf (errout, " (%lu kB/s)",
+					(unsigned long) (Totals.bytes / interval) / 1024L);
+	}
+#endif
+	fputc ('\n', errout);
+
+	fprintf (errout, "%lu tag%s added to tag file",
+			TagFile.numTags.added, plural (TagFile.numTags.added));
+	if (Option.append)
+		fprintf (errout, " (now %lu tags)", totalTags);
+	fputc ('\n', errout);
+
+	if (totalTags > 0  &&  Option.sorted != SO_UNSORTED)
+	{
+		fprintf (errout, "%lu tag%s sorted", totalTags, plural (totalTags));
+#ifdef CLOCK_AVAILABLE
+		fprintf (errout, " in %.02f seconds",
+				((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC);
+#endif
+		fputc ('\n', errout);
+	}
+
+#ifdef DEBUG
+	fprintf (errout, "longest tag line = %lu\n",
+			(unsigned long) TagFile.max.line);
+#endif
+}
+
+static boolean etagsInclude (void)
+{
+	return (boolean)(Option.etags && Option.etagsInclude != NULL);
+}
+
+static void makeTags (cookedArgs *args)
+{
+	clock_t timeStamps [3];
+	boolean resize = FALSE;
+	boolean files = (boolean)(! cArgOff (args) || Option.fileList != NULL
+							  || Option.filter);
+
+	if (! files)
+	{
+		if (filesRequired ())
+			error (FATAL, "No files specified. Try \"%s --help\".",
+				getExecutableName ());
+		else if (! Option.recurse && ! etagsInclude ())
+			return;
+	}
+
+#define timeStamp(n) timeStamps[(n)]=(Option.printTotals ? clock():(clock_t)0)
+	if (! Option.filter)
+		openTagFile ();
+
+	timeStamp (0);
+
+	if (! cArgOff (args))
+	{
+		verbose ("Reading command line arguments\n");
+		resize = createTagsForArgs (args);
+	}
+	if (Option.fileList != NULL)
+	{
+		verbose ("Reading list file\n");
+		resize = (boolean) (createTagsFromListFile (Option.fileList) || resize);
+	}
+	if (Option.filter)
+	{
+		verbose ("Reading filter input\n");
+		resize = (boolean) (createTagsFromFileInput (stdin, TRUE) || resize);
+	}
+	if (! files  &&  Option.recurse)
+		resize = recurseIntoDirectory (".");
+
+	timeStamp (1);
+
+	if (! Option.filter)
+		closeTagFile (resize);
+
+	timeStamp (2);
+
+	if (Option.printTotals)
+		printTotals (timeStamps);
+#undef timeStamp
+}
+
+/*
+ *		Start up code
+ */
+
+extern int main (int argc, char **argv)
+{
+	cookedArgs *args;
+#ifdef VMS
+	extern int getredirection (int *ac, char ***av);
+
+	/* do wildcard expansion and I/O redirection */
+	getredirection (&argc, &argv);
+#endif
+
+#ifdef AMIGA
+	/* This program doesn't work when started from the Workbench */
+	if (argc == 0)
+		exit (1);
+#endif
+
+#ifdef __EMX__
+	_wildcard (&argc, &argv);  /* expand wildcards in argument list */
+#endif
+
+#if defined (macintosh) && BUILD_MPW_TOOL == 0
+	argc = ccommand (&argv);
+#endif
+
+	setCurrentDirectory ();
+	setExecutableName (*argv++);
+	checkRegex ();
+
+	args = cArgNewFromArgv (argv);
+	previewFirstOption (args);
+	testEtagsInvocation ();
+	initializeParsing ();
+	initOptions ();
+	readOptionConfiguration ();
+	verbose ("Reading initial options from command line\n");
+	parseOptions (args);
+	checkOptions ();
+	makeTags (args);
+
+	/*  Clean up.
+	 */
+	cArgDelete (args);
+	freeKeywordTable ();
+	freeRoutineResources ();
+	freeSourceFileResources ();
+	freeTagFileResources ();
+	freeOptionResources ();
+	freeParserResources ();
+	freeRegexResources ();
+
+	exit (0);
+	return 0;
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/options.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/options.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/options.c	(revision 5)
@@ -0,0 +1,1829 @@
+/*
+*   $Id: options.c 576 2007-06-30 04:16:23Z elliotth $
+*
+*   Copyright (c) 1996-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions to process command line options.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>  /* to declare isspace () */
+
+#include "ctags.h"
+#include "debug.h"
+#include "main.h"
+#define OPTION_WRITE
+#include "options.h"
+#include "parse.h"
+#include "routines.h"
+
+/*
+*   MACROS
+*/
+#define INVOCATION  "Usage: %s [options] [file(s)]\n"
+
+#define CTAGS_ENVIRONMENT  "CTAGS"
+#define ETAGS_ENVIRONMENT  "ETAGS"
+
+#define CTAGS_FILE  "tags"
+#define ETAGS_FILE  "TAGS"
+
+#ifndef ETAGS
+# define ETAGS	"etags"  /* name which causes default use of to -e */
+#endif
+
+/*  The following separators are permitted for list options.
+ */
+#define EXTENSION_SEPARATOR '.'
+#define PATTERN_START '('
+#define PATTERN_STOP  ')'
+#define IGNORE_SEPARATORS   ", \t\n"
+
+#ifndef DEFAULT_FILE_FORMAT
+# define DEFAULT_FILE_FORMAT  2
+#endif
+
+#if defined (HAVE_OPENDIR) || defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) || defined (AMIGA)
+# define RECURSE_SUPPORTED
+#endif
+
+#define isCompoundOption(c)  (boolean) (strchr ("fohiILpDb", (c)) != NULL)
+
+/*
+*   Data declarations
+*/
+
+enum eOptionLimits {
+	MaxHeaderExtensions	= 100,  /* maximum number of extensions in -h option */
+	MaxSupportedTagFormat = 2
+};
+
+typedef struct sOptionDescription {
+	int usedByEtags;
+	const char *description;
+} optionDescription;
+
+typedef void (*parametricOptionHandler) (const char *const option, const char *const parameter);
+
+typedef const struct {
+	const char* name;   /* name of option as specified by user */
+	parametricOptionHandler handler;  /* routine to handle option */
+	boolean initOnly;   /* option must be specified before any files */
+} parametricOption;
+
+typedef const struct {
+	const char* name;   /* name of option as specified by user */
+	boolean* pValue;    /* pointer to option value */
+	boolean initOnly;   /* option must be specified before any files */
+} booleanOption;
+
+/*
+*   DATA DEFINITIONS
+*/
+
+static boolean NonOptionEncountered;
+static stringList *OptionFiles;
+static stringList* Excluded;
+static boolean FilesRequired = TRUE;
+static boolean SkipConfiguration;
+
+static const char *const HeaderExtensions [] = {
+	"h", "H", "hh", "hpp", "hxx", "h++", "inc", "def", NULL
+};
+
+optionValues Option = {
+	{
+		FALSE,  /* --extra=f */
+		FALSE,  /* --extra=q */
+		TRUE,   /* --file-scope */
+	},
+	{
+		FALSE,  /* -fields=a */
+		TRUE,   /* -fields=f */
+		FALSE,  /* -fields=m */
+		FALSE,  /* -fields=i */
+		TRUE,   /* -fields=k */
+		FALSE,  /* -fields=z */
+		FALSE,  /* -fields=K */
+		FALSE,  /* -fields=l */
+		FALSE,  /* -fields=n */
+		TRUE,   /* -fields=s */
+		FALSE,  /* -fields=S */
+		TRUE    /* -fields=t */
+	},
+	NULL,       /* -I */
+	FALSE,      /* -a */
+	FALSE,      /* -B */
+	FALSE,      /* -e */
+#ifdef MACROS_USE_PATTERNS
+	EX_PATTERN, /* -n, --excmd */
+#else
+	EX_MIX,     /* -n, --excmd */
+#endif
+	FALSE,      /* -R */
+	SO_SORTED,  /* -u, --sort */
+	FALSE,      /* -V */
+	FALSE,      /* -x */
+	NULL,       /* -L */
+	NULL,       /* -o */
+	NULL,       /* -h */
+	NULL,       /* --etags-include */
+	DEFAULT_FILE_FORMAT,/* --format */
+	FALSE,      /* --if0 */
+	FALSE,      /* --kind-long */
+	LANG_AUTO,  /* --lang */
+	TRUE,       /* --links */
+	FALSE,      /* --filter */
+	NULL,       /* --filter-terminator */
+	FALSE,      /* --tag-relative */
+	FALSE,      /* --totals */
+	FALSE,      /* --line-directives */
+#ifdef DEBUG
+	0, 0        /* -D, -b */
+#endif
+};
+
+/*
+-   Locally used only
+*/
+
+static optionDescription LongOptionDescription [] = {
+ {1,"  -a   Append the tags to an existing tag file."},
+#ifdef DEBUG
+ {1,"  -b <line>"},
+ {1,"       Set break line."},
+#endif
+ {0,"  -B   Use backward searching patterns (?...?)."},
+#ifdef DEBUG
+ {1,"  -D <level>"},
+ {1,"       Set debug level."},
+#endif
+ {0,"  -e   Output tag file for use with Emacs."},
+ {1,"  -f <name>"},
+ {1,"       Write tags to specified file. Value of \"-\" writes tags to stdout"},
+ {1,"       [\"tags\"; or \"TAGS\" when -e supplied]."},
+ {0,"  -F   Use forward searching patterns (/.../) (default)."},
+ {1,"  -h <list>"},
+ {1,"       Specify list of file extensions to be treated as include files."},
+ {1,"       [\".h.H.hh.hpp.hxx.h++\"]."},
+ {1,"  -I <list|@file>"},
+ {1,"       A list of tokens to be specially handled is read from either the"},
+ {1,"       command line or the specified file."},
+ {1,"  -L <file>"},
+ {1,"       A list of source file names are read from the specified file."},
+ {1,"       If specified as \"-\", then standard input is read."},
+ {0,"  -n   Equivalent to --excmd=number."},
+ {0,"  -N   Equivalent to --excmd=pattern."},
+ {1,"  -o   Alternative for -f."},
+#ifdef RECURSE_SUPPORTED
+ {1,"  -R   Equivalent to --recurse."},
+#else
+ {1,"  -R   Not supported on this platform."},
+#endif
+ {0,"  -u   Equivalent to --sort=no."},
+ {1,"  -V   Equivalent to --verbose."},
+ {1,"  -x   Print a tabular cross reference file to standard output."},
+ {1,"  --append=[yes|no]"},
+ {1,"       Should tags should be appended to existing tag file [no]?"},
+ {1,"  --etags-include=file"},
+ {1,"      Include reference to 'file' in Emacs-style tag file (requires -e)."},
+ {1,"  --exclude=pattern"},
+ {1,"      Exclude files and directories matching 'pattern'."},
+ {0,"  --excmd=number|pattern|mix"},
+#ifdef MACROS_USE_PATTERNS
+ {0,"       Uses the specified type of EX command to locate tags [pattern]."},
+#else
+ {0,"       Uses the specified type of EX command to locate tags [mix]."},
+#endif
+ {1,"  --extra=[+|-]flags"},
+ {1,"      Include extra tag entries for selected information (flags: \"fq\")."},
+ {1,"  --fields=[+|-]flags"},
+ {1,"      Include selected extension fields (flags: \"afmikKlnsStz\") [fks]."},
+ {1,"  --file-scope=[yes|no]"},
+ {1,"       Should tags scoped only for a single file (e.g. \"static\" tags"},
+ {1,"       be included in the output [yes]?"},
+ {1,"  --filter=[yes|no]"},
+ {1,"       Behave as a filter, reading file names from standard input and"},
+ {1,"       writing tags to standard output [no]."},
+ {1,"  --filter-terminator=string"},
+ {1,"       Specify string to print to stdout following the tags for each file"},
+ {1,"       parsed when --filter is enabled."},
+ {0,"  --format=level"},
+#if DEFAULT_FILE_FORMAT == 1
+ {0,"       Force output of specified tag file format [1]."},
+#else
+ {0,"       Force output of specified tag file format [2]."},
+#endif
+ {1,"  --help"},
+ {1,"       Print this option summary."},
+ {1,"  --if0=[yes|no]"},
+ {1,"       Should C code within #if 0 conditional branches be parsed [no]?"},
+ {1,"  --<LANG>-kinds=[+|-]kinds"},
+ {1,"       Enable/disable tag kinds for language <LANG>."},
+ {1,"  --langdef=name"},
+ {1,"       Define a new language to be parsed with regular expressions."},
+ {1,"  --langmap=map(s)"},
+ {1,"       Override default mapping of language to source file extension."},
+ {1,"  --language-force=language"},
+ {1,"       Force all files to be interpreted using specified language."},
+ {1,"  --languages=[+|-]list"},
+ {1,"       Restrict files scanned for tags to those mapped to langauges"},
+ {1,"       specified in the comma-separated 'list'. The list can contain any"},
+ {1,"       built-in or user-defined language [all]."},
+ {1,"  --license"},
+ {1,"       Print details of software license."},
+ {0,"  --line-directives=[yes|no]"},
+ {0,"       Should #line directives be processed [no]?"},
+ {1,"  --links=[yes|no]"},
+ {1,"       Indicate whether symbolic links should be followed [yes]."},
+ {1,"  --list-kinds=[language|all]"},
+ {1,"       Output a list of all tag kinds for specified language or all."},
+ {1,"  --list-languages"},
+ {1,"       Output list of supported languages."},
+ {1,"  --list-maps=[language|all]"},
+ {1,"       Output list of language mappings."},
+ {1,"  --options=file"},
+ {1,"       Specify file from which command line options should be read."},
+ {1,"  --recurse=[yes|no]"},
+#ifdef RECURSE_SUPPORTED
+ {1,"       Recurse into directories supplied on command line [no]."},
+#else
+ {1,"       Not supported on this platform."},
+#endif
+#ifdef HAVE_REGEX
+ {1,"  --regex-<LANG>=/line_pattern/name_pattern/[flags]"},
+ {1,"       Define regular expression for locating tags in specific language."},
+#endif
+ {0,"  --sort=[yes|no|foldcase]"},
+ {0,"       Should tags be sorted (optionally ignoring case) [yes]?."},
+ {0,"  --tag-relative=[yes|no]"},
+ {0,"       Should paths be relative to location of tag file [no; yes when -e]?"},
+ {1,"  --totals=[yes|no]"},
+ {1,"       Print statistics about source and tag files [no]."},
+ {1,"  --verbose=[yes|no]"},
+ {1,"       Enable verbose messages describing actions on each source file."},
+ {1,"  --version"},
+ {1,"       Print version identifier to standard output."},
+ {1, NULL}
+};
+
+static const char* const License1 =
+"This program is free software; you can redistribute it and/or\n"
+"modify it under the terms of the GNU General Public License\n"
+"as published by the Free Software Foundation; either version 2\n"
+"of the License, or (at your option) any later version.\n"
+"\n";
+static const char* const License2 =
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+"GNU General Public License for more details.\n"
+"\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program; if not, write to the Free Software\n"
+"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n";
+
+/*  Contains a set of strings describing the set of "features" compiled into
+ *  the code.
+ */
+static const char *const Features [] = {
+#ifdef WIN32
+	"win32",
+#endif
+#ifdef DJGPP
+	"msdos_32",
+#else
+# ifdef MSDOS
+	"msdos_16",
+# endif
+#endif
+#ifdef OS2
+	"os2",
+#endif
+#ifdef AMIGA
+	"amiga",
+#endif
+#ifdef VMS
+	"vms",
+#endif
+#ifdef HAVE_FNMATCH
+	"wildcards",
+#endif
+#ifdef HAVE_REGEX
+	"regex",
+#endif
+#ifndef EXTERNAL_SORT
+	"internal-sort",
+#endif
+#ifdef CUSTOM_CONFIGURATION_FILE
+	"custom-conf",
+#endif
+#if (defined (MSDOS) || defined (WIN32) || defined (OS2)) && defined (UNIX_PATH_SEPARATOR)
+	"unix-path-separator",
+#endif
+#ifdef DEBUG
+	"debug",
+#endif
+	NULL
+};
+
+/*
+*   FUNCTION PROTOTYPES
+*/
+static boolean parseFileOptions (const char *const fileName);
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+extern void verbose (const char *const format, ...)
+{
+	if (Option.verbose)
+	{
+		va_list ap;
+		va_start (ap, format);
+		vprintf (format, ap);
+		va_end (ap);
+	}
+}
+
+static char *stringCopy (const char *const string)
+{
+	char* result = NULL;
+	if (string != NULL)
+		result = eStrdup (string);
+	return result;
+}
+
+static void freeString (char **const pString)
+{
+	if (*pString != NULL)
+	{
+		eFree (*pString);
+		*pString = NULL;
+	}
+}
+
+extern void freeList (stringList** const pList)
+{
+	if (*pList != NULL)
+	{
+		stringListDelete (*pList);
+		*pList = NULL;
+	}
+}
+
+extern void setDefaultTagFileName (void)
+{
+	if (Option.tagFileName != NULL)
+		;  /* accept given name */
+	else if (Option.etags)
+		Option.tagFileName = stringCopy (ETAGS_FILE);
+	else
+		Option.tagFileName = stringCopy (CTAGS_FILE);
+}
+
+extern boolean filesRequired (void)
+{
+	boolean result = FilesRequired;
+	if (Option.recurse)
+		result = FALSE;
+	return result;
+}
+
+extern void checkOptions (void)
+{
+	const char* notice;
+	if (Option.xref)
+	{
+		notice = "xref output";
+		if (Option.include.fileNames)
+		{
+			error (WARNING, "%s disables file name tags", notice);
+			Option.include.fileNames = FALSE;
+		}
+	}
+	if (Option.append)
+	{
+		notice = "append mode is not compatible with";
+		if (isDestinationStdout ())
+			error (FATAL, "%s tags to stdout", notice);
+	}
+	if (Option.filter)
+	{
+		notice = "filter mode";
+		if (Option.printTotals)
+		{
+			error (WARNING, "%s disables totals", notice);
+			Option.printTotals = FALSE;
+		}
+		if (Option.tagFileName != NULL)
+			error (WARNING, "%s ignores output tag file name", notice);
+	}
+}
+
+static void setEtagsMode (void)
+{
+	Option.etags = TRUE;
+	Option.sorted = SO_UNSORTED;
+	Option.lineDirectives = FALSE;
+	Option.tagRelative = TRUE;
+}
+
+extern void testEtagsInvocation (void)
+{
+	char* const execName = eStrdup (getExecutableName ());
+	char* const etags = eStrdup (ETAGS);
+#ifdef CASE_INSENSITIVE_FILENAMES
+	toLowerString (execName);
+	toLowerString (etags);
+#endif
+	if (strstr (execName, etags) != NULL)
+	{
+		verbose ("Running in etags mode\n");
+		setEtagsMode ();
+	}
+	eFree (execName);
+	eFree (etags);
+}
+
+/*
+ *  Cooked argument parsing
+ */
+
+static void parseShortOption (cookedArgs *const args)
+{
+	args->simple [0] = *args->shortOptions++;
+	args->simple [1] = '\0';
+	args->item = args->simple;
+	if (! isCompoundOption (*args->simple))
+		args->parameter = "";
+	else if (*args->shortOptions == '\0')
+	{
+		argForth (args->args);
+		if (argOff (args->args))
+			args->parameter = NULL;
+		else
+			args->parameter = argItem (args->args);
+		args->shortOptions = NULL;
+	}
+	else
+	{
+		args->parameter = args->shortOptions;
+		args->shortOptions = NULL;
+	}
+}
+
+static void parseLongOption (cookedArgs *const args, const char *item)
+{
+	const char* const equal = strchr (item, '=');
+	if (equal == NULL)
+	{
+		args->item = eStrdup (item); /* FIXME: memory leak. */
+		args->parameter = "";
+	}
+	else
+	{
+		const size_t length = equal - item;
+		args->item = xMalloc (length + 1, char); /* FIXME: memory leak. */
+		strncpy (args->item, item, length);
+		args->item [length] = '\0';
+		args->parameter = equal + 1;
+	}
+	Assert (args->item != NULL);
+	Assert (args->parameter != NULL);
+}
+
+static void cArgRead (cookedArgs *const current)
+{
+	char* item;
+
+	Assert (current != NULL);
+	if (! argOff (current->args))
+	{
+		item = argItem (current->args);
+		current->shortOptions = NULL;
+		Assert (item != NULL);
+		if (strncmp (item, "--", (size_t) 2) == 0)
+		{
+			current->isOption = TRUE;
+			current->longOption = TRUE;
+			parseLongOption (current, item + 2);
+			Assert (current->item != NULL);
+			Assert (current->parameter != NULL);
+		}
+		else if (*item == '-')
+		{
+			current->isOption = TRUE;
+			current->longOption = FALSE;
+			current->shortOptions = item + 1;
+			parseShortOption (current);
+		}
+		else
+		{
+			current->isOption = FALSE;
+			current->longOption = FALSE;
+			current->item = item;
+			current->parameter = NULL;
+		}
+	}
+}
+
+extern cookedArgs* cArgNewFromString (const char* string)
+{
+	cookedArgs* const result = xMalloc (1, cookedArgs);
+	memset (result, 0, sizeof (cookedArgs));
+	result->args = argNewFromString (string);
+	cArgRead (result);
+	return result;
+}
+
+extern cookedArgs* cArgNewFromArgv (char* const* const argv)
+{
+	cookedArgs* const result = xMalloc (1, cookedArgs);
+	memset (result, 0, sizeof (cookedArgs));
+	result->args = argNewFromArgv (argv);
+	cArgRead (result);
+	return result;
+}
+
+extern cookedArgs* cArgNewFromFile (FILE* const fp)
+{
+	cookedArgs* const result = xMalloc (1, cookedArgs);
+	memset (result, 0, sizeof (cookedArgs));
+	result->args = argNewFromFile (fp);
+	cArgRead (result);
+	return result;
+}
+
+extern cookedArgs* cArgNewFromLineFile (FILE* const fp)
+{
+	cookedArgs* const result = xMalloc (1, cookedArgs);
+	memset (result, 0, sizeof (cookedArgs));
+	result->args = argNewFromLineFile (fp);
+	cArgRead (result);
+	return result;
+}
+
+extern void cArgDelete (cookedArgs* const current)
+{
+	Assert (current != NULL);
+	argDelete (current->args);
+	memset (current, 0, sizeof (cookedArgs));
+	eFree (current);
+}
+
+static boolean cArgOptionPending (cookedArgs* const current)
+{
+	boolean result = FALSE;
+	if (current->shortOptions != NULL)
+		if (*current->shortOptions != '\0')
+			result = TRUE;
+	return result;
+}
+
+extern boolean cArgOff (cookedArgs* const current)
+{
+	Assert (current != NULL);
+	return (boolean) (argOff (current->args) && ! cArgOptionPending (current));
+}
+
+extern boolean cArgIsOption (cookedArgs* const current)
+{
+	Assert (current != NULL);
+	return current->isOption;
+}
+
+extern const char* cArgItem (cookedArgs* const current)
+{
+	Assert (current != NULL);
+	return current->item;
+}
+
+extern void cArgForth (cookedArgs* const current)
+{
+	Assert (current != NULL);
+	Assert (! cArgOff (current));
+	if (cArgOptionPending (current))
+		parseShortOption (current);
+	else
+	{
+		Assert (! argOff (current->args));
+		argForth (current->args);
+		if (! argOff (current->args))
+			cArgRead (current);
+		else
+		{
+			current->isOption = FALSE;
+			current->longOption = FALSE;
+			current->shortOptions = NULL;
+			current->item = NULL;
+			current->parameter = NULL;
+		}
+	}
+}
+
+/*
+ *  File extension and language mapping
+ */
+
+static void addExtensionList (
+		stringList *const slist, const char *const elist, const boolean clear)
+{
+	char *const extensionList = eStrdup (elist);
+	const char *extension = NULL;
+	boolean first = TRUE;
+
+	if (clear)
+	{
+		verbose ("      clearing\n");
+		stringListClear (slist);
+	}
+	verbose ("      adding: ");
+	if (elist != NULL  &&  *elist != '\0')
+	{
+		extension = extensionList;
+		if (elist [0] == EXTENSION_SEPARATOR)
+			++extension;
+	}
+	while (extension != NULL)
+	{
+		char *separator = strchr (extension, EXTENSION_SEPARATOR);
+		if (separator != NULL)
+			*separator = '\0';
+		verbose ("%s%s", first ? "" : ", ",
+				*extension == '\0' ? "(NONE)" : extension);
+		stringListAdd (slist, vStringNewInit (extension));
+		first = FALSE;
+		if (separator == NULL)
+			extension = NULL;
+		else
+			extension = separator + 1;
+	}
+	if (Option.verbose)
+	{
+		printf ("\n      now: ");
+		stringListPrint (slist);
+		putchar ('\n');
+	}
+	eFree (extensionList);
+}
+
+static boolean isFalse (const char *parameter)
+{
+	return (boolean) (
+		strcasecmp (parameter, "0"  ) == 0  ||
+		strcasecmp (parameter, "n"  ) == 0  ||
+		strcasecmp (parameter, "no" ) == 0  ||
+		strcasecmp (parameter, "off") == 0);
+}
+
+static boolean isTrue (const char *parameter)
+{
+	return (boolean) (
+		strcasecmp (parameter, "1"  ) == 0  ||
+		strcasecmp (parameter, "y"  ) == 0  ||
+		strcasecmp (parameter, "yes") == 0  ||
+		strcasecmp (parameter, "on" ) == 0);
+}
+
+/*  Determines whether the specified file name is considered to be a header
+ *  file for the purposes of determining whether enclosed tags are global or
+ *  static.
+ */
+extern boolean isIncludeFile (const char *const fileName)
+{
+	boolean result = FALSE;
+	const char *const extension = fileExtension (fileName);
+	if (Option.headerExt != NULL)
+		result = stringListExtensionMatched (Option.headerExt, extension);
+	return result;
+}
+
+/*
+ *  Specific option processing
+ */
+
+static void processEtagsInclude (
+		const char *const option, const char *const parameter)
+{
+	if (! Option.etags)
+		error (FATAL, "Etags must be enabled to use \"%s\" option", option);
+	else
+	{
+		vString *const file = vStringNewInit (parameter);
+		if (Option.etagsInclude == NULL)
+			Option.etagsInclude = stringListNew ();
+		stringListAdd (Option.etagsInclude, file);
+		FilesRequired = FALSE;
+	}
+}
+
+static void processExcludeOption (
+		const char *const option, const char *const parameter)
+{
+	const char *const fileName = parameter + 1;
+	if (parameter [0] == '\0')
+		freeList (&Excluded);
+	else if (parameter [0] == '@')
+	{
+		stringList* const sl = stringListNewFromFile (fileName);
+		if (sl == NULL)
+			error (FATAL | PERROR, "cannot open \"%s\"", fileName);
+		if (Excluded == NULL)
+			Excluded = sl;
+		else
+			stringListCombine (Excluded, sl);
+		verbose ("    adding exclude patterns from %s\n", fileName);
+	}
+	else
+	{
+		vString *const item = vStringNewInit (parameter);
+		if (Excluded == NULL)
+			Excluded = stringListNew ();
+		stringListAdd (Excluded, item);
+		verbose ("    adding exclude pattern: %s\n", parameter);
+	}
+}
+
+extern boolean isExcludedFile (const char* const name)
+{
+	const char* base = baseFilename (name);
+	boolean result = FALSE;
+	if (Excluded != NULL)
+	{
+		result = stringListFileMatched (Excluded, base);
+		if (! result  &&  name != base)
+			result = stringListFileMatched (Excluded, name);
+	}
+#ifdef AMIGA
+	/* not a good solution, but the only one which works often */
+	if (! result)
+		result = (boolean) (strcmp (name, TagFile.name) == 0);
+#endif
+	return result;
+}
+
+static void processExcmdOption (
+		const char *const option, const char *const parameter)
+{
+	switch (*parameter)
+	{
+		case 'm': Option.locate = EX_MIX;     break;
+		case 'n': Option.locate = EX_LINENUM; break;
+		case 'p': Option.locate = EX_PATTERN; break;
+		default:
+			error (FATAL, "Invalid value for \"%s\" option", option);
+			break;
+	}
+}
+
+static void processExtraTagsOption (
+		const char *const option, const char *const parameter)
+{
+	struct sInclude *const inc = &Option.include;
+	const char *p = parameter;
+	boolean mode = TRUE;
+	int c;
+
+	if (*p != '+'  &&  *p != '-')
+	{
+		inc->fileNames     = FALSE;
+		inc->qualifiedTags = FALSE;
+#if 0
+		inc->fileScope     = FALSE;
+#endif
+	}
+	while ((c = *p++) != '\0') switch (c)
+	{
+		case '+': mode = TRUE;                break;
+		case '-': mode = FALSE;               break;
+
+		case 'f': inc->fileNames     = mode;  break;
+		case 'q': inc->qualifiedTags = mode;  break;
+#if 0
+		case 'F': inc->fileScope     = mode;  break;
+#endif
+
+		default: error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
+					   c, option);
+			break;
+	}
+}
+
+static void processFieldsOption (
+		const char *const option, const char *const parameter)
+{
+	struct sExtFields *field = &Option.extensionFields;
+	const char *p = parameter;
+	boolean mode = TRUE;
+	int c;
+
+	if (*p != '+'  &&  *p != '-')
+	{
+		field->access           = FALSE;
+		field->fileScope        = FALSE;
+		field->implementation   = FALSE;
+		field->inheritance      = FALSE;
+		field->kind             = FALSE;
+		field->kindKey          = FALSE;
+		field->kindLong         = FALSE;
+		field->language         = FALSE;
+		field->scope            = FALSE;
+		field->typeRef          = FALSE;
+	}
+	while ((c = *p++) != '\0') switch (c)
+	{
+		case '+': mode = TRUE;                  break;
+		case '-': mode = FALSE;                 break;
+
+		case 'a': field->access         = mode; break;
+		case 'f': field->fileScope      = mode; break;
+		case 'm': field->implementation = mode; break;
+		case 'i': field->inheritance    = mode; break;
+		case 'k': field->kind           = mode; break;
+		case 'K': field->kindLong       = mode; break;
+		case 'l': field->language       = mode; break;
+		case 'n': field->lineNumber     = mode; break;
+		case 's': field->scope          = mode; break;
+		case 'S': field->signature      = mode; break;
+		case 'z': field->kindKey        = mode; break;
+		case 't': field->typeRef        = mode; break;
+
+		default: error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
+					c, option);
+			break;
+	}
+}
+
+static void processFilterTerminatorOption (
+		const char *const option, const char *const parameter)
+{
+	freeString (&Option.filterTerminator);
+	Option.filterTerminator = stringCopy (parameter);
+}
+
+static void processFormatOption (
+		const char *const option, const char *const parameter)
+{
+	unsigned int format;
+
+	if (sscanf (parameter, "%u", &format) < 1)
+		error (FATAL, "Invalid value for \"%s\" option",option);
+	else if (format <= (unsigned int) MaxSupportedTagFormat)
+		Option.tagFileFormat = format;
+	else
+		error (FATAL, "Unsupported value for \"%s\" option", option);
+}
+
+static void printInvocationDescription (void)
+{
+	printf (INVOCATION, getExecutableName ());
+}
+
+static void printOptionDescriptions (const optionDescription *const optDesc)
+{
+	int i;
+	for (i = 0 ; optDesc [i].description != NULL ; ++i)
+	{
+		if (! Option.etags || optDesc [i].usedByEtags)
+			puts (optDesc [i].description);
+	}
+}
+
+static void printFeatureList (void)
+{
+	int i;
+
+	for (i = 0 ; Features [i] != NULL ; ++i)
+	{
+		if (i == 0)
+			printf ("  Optional compiled features: ");
+		printf ("%s+%s", (i>0 ? ", " : ""), Features [i]);
+#ifdef CUSTOM_CONFIGURATION_FILE
+		if (strcmp (Features [i], "custom-conf") == 0)
+			printf ("=%s", CUSTOM_CONFIGURATION_FILE);
+#endif
+	}
+	if (i > 0)
+		putchar ('\n');
+}
+
+static void printProgramIdentification (void)
+{
+	printf ("%s %s, %s %s\n",
+	        PROGRAM_NAME, PROGRAM_VERSION,
+	        PROGRAM_COPYRIGHT, AUTHOR_NAME);
+	printf ("  Compiled: %s, %s\n", __DATE__, __TIME__);
+	printf ("  Addresses: <%s>, %s\n", AUTHOR_EMAIL, PROGRAM_URL);
+	printFeatureList ();
+}
+
+static void processHelpOption (
+		const char *const option,
+		const char *const parameter)
+{
+	printProgramIdentification ();
+	putchar ('\n');
+	printInvocationDescription ();
+	putchar ('\n');
+	printOptionDescriptions (LongOptionDescription);
+	exit (0);
+}
+
+static void processLanguageForceOption (
+		const char *const option, const char *const parameter)
+{
+	langType language;
+	if (strcasecmp (parameter, "auto") == 0)
+		language = LANG_AUTO;
+	else
+		language = getNamedLanguage (parameter);
+
+	if (strcmp (option, "lang") == 0  ||  strcmp (option, "language") == 0)
+		error (WARNING,
+			   "\"--%s\" option is obsolete; use \"--language-force\" instead",
+			   option);
+	if (language == LANG_IGNORE)
+		error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
+	else
+		Option.language = language;
+}
+static char* skipPastMap (char* p)
+{
+	while (*p != EXTENSION_SEPARATOR  &&
+			*p != PATTERN_START  &&  *p != ','  &&  *p != '\0')
+		++p;
+	return p;
+}
+
+/* Parses the mapping beginning at `map', adds it to the language map, and
+ * returns first character past the map.
+ */
+static char* addLanguageMap (const langType language, char* map)
+{
+	char* p = NULL;
+	const char first = *map;
+	if (first == EXTENSION_SEPARATOR)  /* extension map */
+	{
+		++map;
+		p = skipPastMap (map);
+		if (*p == '\0')
+		{
+			verbose (" .%s", map);
+			addLanguageExtensionMap (language, map);
+			p = map + strlen (map);
+		}
+		else
+		{
+			const char separator = *p;
+			*p = '\0';
+			verbose (" .%s", map);
+			addLanguageExtensionMap (language, map);
+			*p = separator;
+		}
+	}
+	else if (first == PATTERN_START)  /* pattern map */
+	{
+		++map;
+		for (p = map  ;  *p != PATTERN_STOP  &&  *p != '\0'  ;  ++p)
+		{
+			if (*p == '\\'  &&  *(p + 1) == PATTERN_STOP)
+				++p;
+		}
+		if (*p == '\0')
+			error (FATAL, "Unterminated file name pattern for %s language",
+			   getLanguageName (language));
+		else
+		{
+			*p++ = '\0';
+			verbose (" (%s)", map);
+			addLanguagePatternMap (language, map);
+		}
+	}
+	else
+		error (FATAL, "Badly formed language map for %s language",
+				getLanguageName (language));
+	return p;
+}
+
+static char* processLanguageMap (char* map)
+{
+	char* const separator = strchr (map, ':');
+	char* result = NULL;
+	if (separator != NULL)
+	{
+		langType language;
+		char *list = separator + 1;
+		boolean clear = FALSE;
+		*separator = '\0';
+		language = getNamedLanguage (map);
+		if (language != LANG_IGNORE)
+		{
+			const char *const deflt = "default";
+			char* p;
+			if (*list == '+')
+				++list;
+			else
+				clear = TRUE;
+			for (p = list  ;  *p != ','  &&  *p != '\0'  ;  ++p)  /*no-op*/ ;
+			if ((size_t) (p - list) == strlen (deflt) &&
+				strncasecmp (list, deflt, p - list) == 0)
+			{
+				verbose ("    Restoring default %s language map: ", getLanguageName (language));
+				installLanguageMapDefault (language);
+				list = p;
+			}
+			else
+			{
+				if (clear)
+				{
+					verbose ("    Setting %s language map:", getLanguageName (language));
+					clearLanguageMap (language);
+				}
+				else
+					verbose ("    Adding to %s language map:", getLanguageName (language));
+				while (list != NULL  &&  *list != '\0'  &&  *list != ',')
+					list = addLanguageMap (language, list);
+				verbose ("\n");
+			}
+			if (list != NULL  &&  *list == ',')
+				++list;
+			result = list;
+		}
+	}
+	return result;
+}
+
+static void processLanguageMapOption (
+		const char *const option, const char *const parameter)
+{
+	char *const maps = eStrdup (parameter);
+	char *map = maps;
+
+	if (strcmp (parameter, "default") == 0)
+	{
+		verbose ("    Restoring default language maps:\n");
+		installLanguageMapDefaults ();
+	}
+	else while (map != NULL  &&  *map != '\0')
+	{
+		char* const next = processLanguageMap (map);
+		if (next == NULL)
+			error (WARNING, "Unknown language \"%s\" in \"%s\" option", parameter, option);
+		map = next;
+	}
+	eFree (maps);
+}
+
+static void processLanguagesOption (
+		const char *const option, const char *const parameter)
+{
+	char *const langs = eStrdup (parameter);
+	enum { Add, Remove, Replace } mode = Replace;
+	boolean first = TRUE;
+	char *lang = langs;
+	const char* prefix = "";
+	verbose ("    Enabled languages: ");
+	while (lang != NULL)
+	{
+		char *const end = strchr (lang, ',');
+		if (lang [0] == '+')
+		{
+			++lang;
+			mode = Add;
+			prefix = "+ ";
+		}
+		else if (lang [0] == '-')
+		{
+			++lang;
+			mode = Remove;
+			prefix = "- ";
+		}
+		if (mode == Replace)
+			enableLanguages (FALSE);
+		if (end != NULL)
+			*end = '\0';
+		if (lang [0] != '\0')
+		{
+			if (strcmp (lang, "all") == 0)
+				enableLanguages ((boolean) (mode != Remove));
+			else
+			{
+				const langType language = getNamedLanguage (lang);
+				if (language == LANG_IGNORE)
+					error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option);
+				else
+					enableLanguage (language, (boolean) (mode != Remove));
+			}
+			verbose ("%s%s%s", (first ? "" : ", "), prefix, lang);
+			prefix = "";
+			first = FALSE;
+			if (mode == Replace)
+				mode = Add;
+		}
+		lang = (end != NULL ? end + 1 : NULL);
+	}
+	verbose ("\n");
+	eFree (langs);
+}
+
+static void processLicenseOption (
+		const char *const option,
+		const char *const parameter)
+{
+	printProgramIdentification ();
+	puts ("");
+	puts (License1);
+	puts (License2);
+	exit (0);
+}
+
+static void processListKindsOption (
+		const char *const option, const char *const parameter)
+{
+	if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0)
+	    printLanguageKinds (LANG_AUTO);
+	else
+	{
+		langType language = getNamedLanguage (parameter);
+		if (language == LANG_IGNORE)
+			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
+		else
+			printLanguageKinds (language);
+	}
+	exit (0);
+}
+
+static void processListMapsOption (
+		const char *const option,
+		const char *const parameter)
+{
+	if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0)
+	    printLanguageMaps (LANG_AUTO);
+	else
+	{
+		langType language = getNamedLanguage (parameter);
+		if (language == LANG_IGNORE)
+			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
+		else
+			printLanguageMaps (language);
+	}
+	exit (0);
+}
+
+static void processListLanguagesOption (
+		const char *const option,
+		const char *const parameter)
+{
+	printLanguageList ();
+	exit (0);
+}
+
+static void processOptionFile (
+		const char *const option, const char *const parameter)
+{
+	if (parameter [0] == '\0')
+		error (WARNING, "no option file supplied for \"%s\"", option);
+	else if (! parseFileOptions (parameter))
+		error (FATAL | PERROR, "cannot open option file \"%s\"", parameter);
+}
+
+static void processSortOption (
+		const char *const option, const char *const parameter)
+{
+	if (isFalse (parameter))
+		Option.sorted = SO_UNSORTED;
+	else if (isTrue (parameter))
+		Option.sorted = SO_SORTED;
+	else if (strcasecmp (parameter, "f") == 0 ||
+			strcasecmp (parameter, "fold") == 0 ||
+			strcasecmp (parameter, "foldcase") == 0)
+		Option.sorted = SO_FOLDSORTED;
+	else
+		error (FATAL, "Invalid value for \"%s\" option", option);
+}
+
+static void installHeaderListDefaults (void)
+{
+	Option.headerExt = stringListNewFromArgv (HeaderExtensions);
+	if (Option.verbose)
+	{
+		printf ("    Setting default header extensions: ");
+		stringListPrint (Option.headerExt);
+		putchar ('\n');
+	}
+}
+
+static void processHeaderListOption (const int option, const char *parameter)
+{
+	/*  Check to make sure that the user did not enter "ctags -h *.c"
+	 *  by testing to see if the list is a filename that exists.
+	 */
+	if (doesFileExist (parameter))
+		error (FATAL, "-%c: Invalid list", option);
+	if (strcmp (parameter, "default") == 0)
+		installHeaderListDefaults ();
+	else
+	{
+		boolean clear = TRUE;
+
+		if (parameter [0] == '+')
+		{
+			++parameter;
+			clear = FALSE;
+		}
+		if (Option.headerExt == NULL)
+			Option.headerExt = stringListNew ();
+		verbose ("    Header Extensions:\n");
+		addExtensionList (Option.headerExt, parameter, clear);
+	}
+}
+
+/*
+ *  Token ignore processing
+ */
+
+/*  Determines whether or not "name" should be ignored, per the ignore list.
+ */
+extern boolean isIgnoreToken (
+		const char *const name, boolean *const pIgnoreParens,
+		const char **const replacement)
+{
+	boolean result = FALSE;
+
+	if (Option.ignore != NULL)
+	{
+		const size_t nameLen = strlen (name);
+		unsigned int i;
+
+		if (pIgnoreParens != NULL)
+			*pIgnoreParens = FALSE;
+
+		for (i = 0  ;  i < stringListCount (Option.ignore)  ;  ++i)
+		{
+			vString *token = stringListItem (Option.ignore, i);
+
+			if (strncmp (vStringValue (token), name, nameLen) == 0)
+			{
+				const size_t tokenLen = vStringLength (token);
+
+				if (nameLen == tokenLen)
+				{
+					result = TRUE;
+					break;
+				}
+				else if (tokenLen == nameLen + 1  &&
+						vStringChar (token, tokenLen - 1) == '+')
+				{
+					result = TRUE;
+					if (pIgnoreParens != NULL)
+						*pIgnoreParens = TRUE;
+					break;
+				}
+				else if (vStringChar (token, nameLen) == '=')
+				{
+					if (replacement != NULL)
+						*replacement = vStringValue (token) + nameLen + 1;
+					break;
+				}
+			}
+		}
+	}
+	return result;
+}
+
+static void saveIgnoreToken (vString *const ignoreToken)
+{
+	if (Option.ignore == NULL)
+		Option.ignore = stringListNew ();
+	stringListAdd (Option.ignore, ignoreToken);
+	verbose ("    ignore token: %s\n", vStringValue (ignoreToken));
+}
+
+static void readIgnoreList (const char *const list)
+{
+	char* newList = stringCopy (list);
+	const char *token = strtok (newList, IGNORE_SEPARATORS);
+
+	while (token != NULL)
+	{
+		vString *const entry = vStringNewInit (token);
+
+		saveIgnoreToken (entry);
+		token = strtok (NULL, IGNORE_SEPARATORS);
+	}
+	eFree (newList);
+}
+
+static void addIgnoreListFromFile (const char *const fileName)
+{
+	stringList* tokens = stringListNewFromFile (fileName);
+	if (tokens == NULL)
+		error (FATAL | PERROR, "cannot open \"%s\"", fileName);
+	if (Option.ignore == NULL)
+		Option.ignore = tokens;
+	else
+		stringListCombine (Option.ignore, tokens);
+}
+
+static void processIgnoreOption (const char *const list)
+{
+	if (strchr ("@./\\", list [0]) != NULL)
+	{
+		const char* fileName = (*list == '@') ? list + 1 : list;
+		addIgnoreListFromFile (fileName);
+	}
+#if defined (MSDOS) || defined (WIN32) || defined (OS2)
+	else if (isalpha (list [0])  &&  list [1] == ':')
+		addIgnoreListFromFile (list);
+#endif
+	else if (strcmp (list, "-") == 0)
+	{
+		freeList (&Option.ignore);
+		verbose ("    clearing list\n");
+	}
+	else
+		readIgnoreList (list);
+}
+
+static void processVersionOption (
+		const char *const option,
+		const char *const parameter)
+{
+	printProgramIdentification ();
+	exit (0);
+}
+
+/*
+ *  Option tables
+ */
+
+static parametricOption ParametricOptions [] = {
+	{ "etags-include",          processEtagsInclude,            FALSE   },
+	{ "exclude",                processExcludeOption,           FALSE   },
+	{ "excmd",                  processExcmdOption,             FALSE   },
+	{ "extra",                  processExtraTagsOption,         FALSE   },
+	{ "fields",                 processFieldsOption,            FALSE   },
+	{ "filter-terminator",      processFilterTerminatorOption,  TRUE    },
+	{ "format",                 processFormatOption,            TRUE    },
+	{ "help",                   processHelpOption,              TRUE    },
+	{ "lang",                   processLanguageForceOption,     FALSE   },
+	{ "language",               processLanguageForceOption,     FALSE   },
+	{ "language-force",         processLanguageForceOption,     FALSE   },
+	{ "languages",              processLanguagesOption,         FALSE   },
+	{ "langdef",                processLanguageDefineOption,    FALSE   },
+	{ "langmap",                processLanguageMapOption,       FALSE   },
+	{ "license",                processLicenseOption,           TRUE    },
+	{ "list-kinds",             processListKindsOption,         TRUE    },
+	{ "list-maps",              processListMapsOption,          TRUE    },
+	{ "list-languages",         processListLanguagesOption,     TRUE    },
+	{ "options",                processOptionFile,              FALSE   },
+	{ "sort",                   processSortOption,              TRUE    },
+	{ "version",                processVersionOption,           TRUE    },
+};
+
+static booleanOption BooleanOptions [] = {
+	{ "append",         &Option.append,                 TRUE    },
+	{ "file-scope",     &Option.include.fileScope,      FALSE   },
+	{ "file-tags",      &Option.include.fileNames,      FALSE   },
+	{ "filter",         &Option.filter,                 TRUE    },
+	{ "if0",            &Option.if0,                    FALSE   },
+	{ "kind-long",      &Option.kindLong,               TRUE    },
+	{ "line-directives",&Option.lineDirectives,         FALSE   },
+	{ "links",          &Option.followLinks,            FALSE   },
+#ifdef RECURSE_SUPPORTED
+	{ "recurse",        &Option.recurse,                FALSE   },
+#endif
+	{ "tag-relative",   &Option.tagRelative,            TRUE    },
+	{ "totals",         &Option.printTotals,            TRUE    },
+	{ "verbose",        &Option.verbose,                FALSE   },
+};
+
+/*
+ *  Generic option parsing
+ */
+
+static void checkOptionOrder (const char* const option)
+{
+	if (NonOptionEncountered)
+		error (FATAL, "-%s option may not follow a file name", option);
+}
+
+static boolean processParametricOption (
+		const char *const option, const char *const parameter)
+{
+	const int count = sizeof (ParametricOptions) / sizeof (parametricOption);
+	boolean found = FALSE;
+	int i;
+
+	for (i = 0  ;  i < count  &&  ! found  ;  ++i)
+	{
+		parametricOption* const entry = &ParametricOptions [i];
+		if (strcmp (option, entry->name) == 0)
+		{
+			found = TRUE;
+			if (entry->initOnly)
+				checkOptionOrder (option);
+			(entry->handler) (option, parameter);
+		}
+	}
+	return found;
+}
+
+static boolean getBooleanOption (
+		const char *const option, const char *const parameter)
+{
+	boolean selection = TRUE;
+
+	if (parameter [0] == '\0')
+		selection = TRUE;
+	else if (isFalse (parameter))
+		selection = FALSE;
+	else if (isTrue (parameter))
+		selection = TRUE;
+	else
+		error (FATAL, "Invalid value for \"%s\" option", option);
+
+	return selection;
+}
+
+static boolean processBooleanOption (
+		const char *const option, const char *const parameter)
+{
+	const int count = sizeof (BooleanOptions) / sizeof (booleanOption);
+	boolean found = FALSE;
+	int i;
+
+	for (i = 0  ;  i < count  &&  ! found  ;  ++i)
+	{
+		booleanOption* const entry = &BooleanOptions [i];
+		if (strcmp (option, entry->name) == 0)
+		{
+			found = TRUE;
+			if (entry->initOnly)
+				checkOptionOrder (option);
+			*entry->pValue = getBooleanOption (option, parameter);
+		}
+	}
+	return found;
+}
+
+static void processLongOption (
+		const char *const option, const char *const parameter)
+{
+	Assert (parameter != NULL);
+	if (parameter == NULL  &&  parameter [0] == '\0')
+		verbose ("  Option: --%s\n", option);
+	else
+		verbose ("  Option: --%s=%s\n", option, parameter);
+
+	if (processBooleanOption (option, parameter))
+		;
+	else if (processParametricOption (option, parameter))
+		;
+	else if (processKindOption (option, parameter))
+		;
+	else if (processRegexOption (option, parameter))
+		;
+#ifndef RECURSE_SUPPORTED
+	else if (strcmp (option, "recurse") == 0)
+		error (WARNING, "%s option not supported on this host", option);
+#endif
+	else
+		error (FATAL, "Unknown option: --%s", option);
+}
+
+static void processShortOption (
+		const char *const option, const char *const parameter)
+{
+	if (parameter == NULL  ||  parameter [0] == '\0')
+		verbose ("  Option: -%s\n", option);
+	else
+		verbose ("  Option: -%s %s\n", option, parameter);
+
+	if (isCompoundOption (*option) && (parameter == NULL  ||  parameter [0] == '\0'))
+		error (FATAL, "Missing parameter for \"%s\" option", option);
+	else switch (*option)
+	{
+		case '?':
+			processHelpOption ("?", NULL);
+			exit (0);
+			break;
+		case 'a':
+			checkOptionOrder (option);
+			Option.append = TRUE;
+			break;
+#ifdef DEBUG
+		case 'b':
+			if (atol (parameter) < 0)
+				error (FATAL, "-%s: Invalid line number", option);
+			Option.breakLine = atol (parameter);
+			break;
+		case 'D':
+			Option.debugLevel = strtol (parameter, NULL, 0);
+			if (debug (DEBUG_STATUS))
+				Option.verbose = TRUE;
+			break;
+#endif
+		case 'B':
+			Option.backward = TRUE;
+			break;
+		case 'e':
+			checkOptionOrder (option);
+			setEtagsMode ();
+			break;
+		case 'f':
+		case 'o':
+			checkOptionOrder (option);
+			if (Option.tagFileName != NULL)
+			{
+				error (WARNING,
+					"-%s option specified more than once, last value used",
+					option);
+				freeString (&Option.tagFileName);
+			}
+			else if (parameter [0] == '-'  &&  parameter [1] != '\0')
+				error (FATAL, "output file name may not begin with a '-'");
+			Option.tagFileName = stringCopy (parameter);
+			break;
+		case 'F':
+			Option.backward = FALSE;
+			break;
+		case 'h':
+			processHeaderListOption (*option, parameter);
+			break;
+		case 'I':
+			processIgnoreOption (parameter);
+			break;
+		case 'L':
+			if (Option.fileList != NULL)
+			{
+				error (WARNING,
+					"-%s option specified more than once, last value used",
+					option);
+				freeString (&Option.fileList);
+			}
+			Option.fileList = stringCopy (parameter);
+			break;
+		case 'n':
+			Option.locate = EX_LINENUM;
+			break;
+		case 'N':
+			Option.locate = EX_PATTERN;
+			break;
+		case 'R':
+#ifdef RECURSE_SUPPORTED
+			Option.recurse = TRUE;
+#else
+			error (WARNING, "-%s option not supported on this host", option);
+#endif
+			break;
+		case 'u':
+			checkOptionOrder (option);
+			Option.sorted = SO_UNSORTED;
+			break;
+		case 'V':
+			Option.verbose = TRUE;
+			break;
+		case 'w':
+			/* silently ignored */
+			break;
+		case 'x':
+			checkOptionOrder (option);
+			Option.xref = TRUE;
+			break;
+		default:
+			error (FATAL, "Unknown option: -%s", option);
+			break;
+	}
+}
+
+extern void parseOption (cookedArgs* const args)
+{
+	Assert (! cArgOff (args));
+	if (args->isOption)
+	{
+		if (args->longOption)
+			processLongOption (args->item, args->parameter);
+		else
+		{
+			const char *parameter = args->parameter;
+			while (*parameter == ' ')
+				++parameter;
+			processShortOption (args->item, parameter);
+		}
+		cArgForth (args);
+	}
+}
+
+extern void parseOptions (cookedArgs* const args)
+{
+	NonOptionEncountered = FALSE;
+	while (! cArgOff (args)  &&  cArgIsOption (args))
+		parseOption (args);
+	if (! cArgOff (args)  &&  ! cArgIsOption (args))
+		NonOptionEncountered = TRUE;
+}
+
+static const char *CheckFile;
+static boolean checkSameFile (const char *const fileName)
+{
+	return isSameFile (CheckFile, fileName);
+}
+
+static boolean parseFileOptions (const char* const fileName)
+{
+	boolean fileFound = FALSE;
+	const char* const format = "Considering option file %s: %s\n";
+	CheckFile = fileName;
+	if (stringListHasTest (OptionFiles, checkSameFile))
+		verbose (format, fileName, "already considered");
+	else
+	{
+		FILE* const fp = fopen (fileName, "r");
+		if (fp == NULL)
+			verbose (format, fileName, "not found");
+		else
+		{
+			cookedArgs* const args = cArgNewFromLineFile (fp);
+			vString* file = vStringNewInit (fileName);
+			stringListAdd (OptionFiles, file);
+			verbose (format, fileName, "reading...");
+			parseOptions (args);
+			if (NonOptionEncountered)
+				error (WARNING, "Ignoring non-option in %s\n", fileName);
+			cArgDelete (args);
+			fclose (fp);
+			fileFound = TRUE;
+		}
+	}
+	return fileFound;
+}
+
+/* Actions to be taken before reading any other options */
+extern void previewFirstOption (cookedArgs* const args)
+{
+	while (cArgIsOption (args))
+	{
+		if (strcmp (args->item, "V") == 0 || strcmp (args->item, "verbose") == 0)
+			parseOption (args);
+		else if (strcmp (args->item, "options") == 0  &&
+				strcmp (args->parameter, "NONE") == 0)
+		{
+			fprintf (stderr, "No options will be read from files or environment\n");
+			SkipConfiguration = TRUE;
+			cArgForth (args);
+		}
+		else
+			break;
+	}
+}
+
+static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname)
+{
+	vString* const pathname = combinePathAndFile (directory, leafname);
+	parseFileOptions (vStringValue (pathname));
+	vStringDelete (pathname);
+}
+
+static void parseConfigurationFileOptionsInDirectory (const char* directory)
+{
+	parseConfigurationFileOptionsInDirectoryWithLeafname (directory, ".ctags");
+#ifdef MSDOS_STYLE_PATH
+	parseConfigurationFileOptionsInDirectoryWithLeafname (directory, "ctags.cnf");
+#endif
+}
+
+static void parseConfigurationFileOptions (void)
+{
+	/* We parse .ctags on all systems, and additionally ctags.cnf on DOS. */
+	const char* const home = getenv ("HOME");
+#ifdef CUSTOM_CONFIGURATION_FILE
+	parseFileOptions (CUSTOM_CONFIGURATION_FILE);
+#endif
+#ifdef MSDOS_STYLE_PATH
+	parseFileOptions ("/ctags.cnf");
+#endif
+	parseFileOptions ("/etc/ctags.conf");
+	parseFileOptions ("/usr/local/etc/ctags.conf");
+	if (home != NULL)
+	{
+		parseConfigurationFileOptionsInDirectory (home);
+	}
+	else
+	{
+#ifdef MSDOS_STYLE_PATH
+		/*
+		 * Windows users don't usually set HOME.
+		 * The OS sets HOMEDRIVE and HOMEPATH for them.
+		 */
+		const char* homeDrive = getenv ("HOMEDRIVE");
+		const char* homePath = getenv ("HOMEPATH");
+		if (homeDrive != NULL && homePath != NULL)
+		{
+			vString* const windowsHome = vStringNew ();
+			vStringCatS (windowsHome, homeDrive);
+			vStringCatS (windowsHome, homePath);
+			parseConfigurationFileOptionsInDirectory (vStringValue (windowsHome));
+			vStringDelete (windowsHome);
+		}
+#endif
+	}
+	parseConfigurationFileOptionsInDirectory (".");
+}
+
+static void parseEnvironmentOptions (void)
+{
+	const char *envOptions = NULL;
+	const char* var = NULL;
+
+	if (Option.etags)
+	{
+		var = ETAGS_ENVIRONMENT;
+		envOptions = getenv (var);
+	}
+	if (envOptions == NULL)
+	{
+		var = CTAGS_ENVIRONMENT;
+		envOptions = getenv (var);
+	}
+	if (envOptions != NULL  &&  envOptions [0] != '\0')
+	{
+		cookedArgs* const args = cArgNewFromString (envOptions);
+		verbose ("Reading options from $CTAGS\n");
+		parseOptions (args);
+		cArgDelete (args);
+		if (NonOptionEncountered)
+			error (WARNING, "Ignoring non-option in %s variable", var);
+	}
+}
+
+extern void readOptionConfiguration (void)
+{
+	if (! SkipConfiguration)
+	{
+		parseConfigurationFileOptions ();
+		parseEnvironmentOptions ();
+	}
+}
+
+/*
+*   Option initialization
+*/
+
+extern void initOptions (void)
+{
+	OptionFiles = stringListNew ();
+	verbose ("Setting option defaults\n");
+	installHeaderListDefaults ();
+	verbose ("  Installing default language mappings:\n");
+	installLanguageMapDefaults ();
+
+	/* always excluded by default */
+	verbose ("  Installing default exclude patterns:\n");
+	processExcludeOption (NULL, "{arch}");
+	processExcludeOption (NULL, ".arch-ids");
+	processExcludeOption (NULL, ".arch-inventory");
+	processExcludeOption (NULL, "autom4te.cache");
+	processExcludeOption (NULL, "BitKeeper");
+	processExcludeOption (NULL, ".bzr");
+	processExcludeOption (NULL, ".bzrignore");
+	processExcludeOption (NULL, "CVS");
+	processExcludeOption (NULL, ".cvsignore");
+	processExcludeOption (NULL, "_darcs");
+	processExcludeOption (NULL, ".deps");
+	processExcludeOption (NULL, "EIFGEN");
+	processExcludeOption (NULL, ".git");
+	processExcludeOption (NULL, ".hg");
+	processExcludeOption (NULL, "PENDING");
+	processExcludeOption (NULL, "RCS");
+	processExcludeOption (NULL, "RESYNC");
+	processExcludeOption (NULL, "SCCS");
+	processExcludeOption (NULL, ".svn");
+}
+
+extern void freeOptionResources (void)
+{
+	freeString (&Option.tagFileName);
+	freeString (&Option.fileList);
+	freeString (&Option.filterTerminator);
+
+	freeList (&Excluded);
+	freeList (&Option.ignore);
+	freeList (&Option.headerExt);
+	freeList (&Option.etagsInclude);
+	freeList (&OptionFiles);
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/parse.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/parse.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/parse.c	(revision 5)
@@ -0,0 +1,677 @@
+/*
+*   $Id: parse.c 597 2007-07-31 05:35:30Z dhiebert $
+*
+*   Copyright (c) 1996-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions for managing source languages and
+*   dispatching files to the appropriate language parser.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include <string.h>
+
+#include "debug.h"
+#include "entry.h"
+#include "main.h"
+#define OPTION_WRITE
+#include "options.h"
+#include "parsers.h" 
+#include "read.h"
+#include "routines.h"
+#include "vstring.h"
+
+/*
+*   DATA DEFINITIONS
+*/
+static parserDefinitionFunc* BuiltInParsers[] = { PARSER_LIST };
+static parserDefinition** LanguageTable = NULL;
+static unsigned int LanguageCount = 0;
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+extern void makeSimpleTag (
+		const vString* const name, kindOption* const kinds, const int kind)
+{
+	if (kinds [kind].enabled  &&  name != NULL  &&  vStringLength (name) > 0)
+	{
+	    tagEntryInfo e;
+	    initTagEntry (&e, vStringValue (name));
+
+	    e.kindName = kinds [kind].name;
+	    e.kind     = kinds [kind].letter;
+
+	    makeTagEntry (&e);
+	}
+}
+
+/*
+*   parserDescription mapping management
+*/
+
+extern parserDefinition* parserNew (const char* name)
+{
+	parserDefinition* result = xCalloc (1, parserDefinition);
+	result->name = eStrdup (name);
+	return result;
+}
+
+extern const char *getLanguageName (const langType language)
+{
+	const char* result;
+	if (language == LANG_IGNORE)
+		result = "unknown";
+	else
+	{
+		Assert (0 <= language  &&  language < (int) LanguageCount);
+		result = LanguageTable [language]->name;
+	}
+	return result;
+}
+
+extern langType getNamedLanguage (const char *const name)
+{
+	langType result = LANG_IGNORE;
+	unsigned int i;
+	Assert (name != NULL);
+	for (i = 0  ;  i < LanguageCount  &&  result == LANG_IGNORE  ;  ++i)
+	{
+		const parserDefinition* const lang = LanguageTable [i];
+		if (lang->name != NULL)
+			if (strcasecmp (name, lang->name) == 0)
+				result = i;
+	}
+	return result;
+}
+
+static langType getExtensionLanguage (const char *const extension)
+{
+	langType result = LANG_IGNORE;
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  &&  result == LANG_IGNORE  ;  ++i)
+	{
+		stringList* const exts = LanguageTable [i]->currentExtensions;
+		if (exts != NULL  &&  stringListExtensionMatched (exts, extension))
+			result = i;
+	}
+	return result;
+}
+
+static langType getPatternLanguage (const char *const fileName)
+{
+	langType result = LANG_IGNORE;
+	const char* base = baseFilename (fileName);
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  &&  result == LANG_IGNORE  ;  ++i)
+	{
+		stringList* const ptrns = LanguageTable [i]->currentPatterns;
+		if (ptrns != NULL  &&  stringListFileMatched (ptrns, base))
+			result = i;
+	}
+	return result;
+}
+
+#ifdef SYS_INTERPRETER
+
+/*  The name of the language interpreter, either directly or as the argument
+ *  to "env".
+ */
+static vString* determineInterpreter (const char* const cmd)
+{
+	vString* const interpreter = vStringNew ();
+	const char* p = cmd;
+	do
+	{
+		vStringClear (interpreter);
+		for ( ;  isspace ((int) *p)  ;  ++p)
+			;  /* no-op */
+		for ( ;  *p != '\0'  &&  ! isspace ((int) *p)  ;  ++p)
+			vStringPut (interpreter, (int) *p);
+		vStringTerminate (interpreter);
+	} while (strcmp (vStringValue (interpreter), "env") == 0);
+	return interpreter;
+}
+
+static langType getInterpreterLanguage (const char *const fileName)
+{
+	langType result = LANG_IGNORE;
+	FILE* const fp = fopen (fileName, "r");
+	if (fp != NULL)
+	{
+		vString* const vLine = vStringNew ();
+		const char* const line = readLine (vLine, fp);
+		if (line != NULL  &&  line [0] == '#'  &&  line [1] == '!')
+		{
+			const char* const lastSlash = strrchr (line, '/');
+			const char *const cmd = lastSlash != NULL ? lastSlash+1 : line+2;
+			vString* const interpreter = determineInterpreter (cmd);
+			result = getExtensionLanguage (vStringValue (interpreter));
+			if (result == LANG_IGNORE)
+				result = getNamedLanguage (vStringValue (interpreter));
+			vStringDelete (interpreter);
+		}
+		vStringDelete (vLine);
+		fclose (fp);
+	}
+	return result;
+}
+
+#endif
+
+extern langType getFileLanguage (const char *const fileName)
+{
+	langType language = Option.language;
+	if (language == LANG_AUTO)
+	{
+		language = getExtensionLanguage (fileExtension (fileName));
+		if (language == LANG_IGNORE)
+			language = getPatternLanguage (fileName);
+#ifdef SYS_INTERPRETER
+		if (language == LANG_IGNORE)
+		{
+			fileStatus *status = eStat (fileName);
+			if (status->isExecutable)
+				language = getInterpreterLanguage (fileName);
+		}
+#endif
+	}
+	return language;
+}
+
+extern void printLanguageMap (const langType language)
+{
+	boolean first = TRUE;
+	unsigned int i;
+	stringList* map = LanguageTable [language]->currentPatterns;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	for (i = 0  ;  map != NULL  &&  i < stringListCount (map)  ;  ++i)
+	{
+		printf ("%s(%s)", (first ? "" : " "),
+				vStringValue (stringListItem (map, i)));
+		first = FALSE;
+	}
+	map = LanguageTable [language]->currentExtensions;
+	for (i = 0  ;  map != NULL  &&  i < stringListCount (map)  ;  ++i)
+	{
+		printf ("%s.%s", (first ? "" : " "),
+				vStringValue (stringListItem (map, i)));
+		first = FALSE;
+	}
+}
+
+extern void installLanguageMapDefault (const langType language)
+{
+	parserDefinition* lang;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	if (lang->currentPatterns != NULL)
+		stringListDelete (lang->currentPatterns);
+	if (lang->currentExtensions != NULL)
+		stringListDelete (lang->currentExtensions);
+
+	if (lang->patterns == NULL)
+		lang->currentPatterns = stringListNew ();
+	else
+	{
+		lang->currentPatterns =
+			stringListNewFromArgv (lang->patterns);
+	}
+	if (lang->extensions == NULL)
+		lang->currentExtensions = stringListNew ();
+	else
+	{
+		lang->currentExtensions =
+			stringListNewFromArgv (lang->extensions);
+	}
+	if (Option.verbose)
+		printLanguageMap (language);
+	verbose ("\n");
+}
+
+extern void installLanguageMapDefaults (void)
+{
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  ;  ++i)
+	{
+		verbose ("    %s: ", getLanguageName (i));
+		installLanguageMapDefault (i);
+	}
+}
+
+extern void clearLanguageMap (const langType language)
+{
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	stringListClear (LanguageTable [language]->currentPatterns);
+	stringListClear (LanguageTable [language]->currentExtensions);
+}
+
+extern void addLanguagePatternMap (const langType language, const char* ptrn)
+{
+	vString* const str = vStringNewInit (ptrn);
+	parserDefinition* lang;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	if (lang->currentPatterns == NULL)
+		lang->currentPatterns = stringListNew ();
+	stringListAdd (lang->currentPatterns, str);
+}
+
+extern boolean removeLanguageExtensionMap (const char *const extension)
+{
+	boolean result = FALSE;
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  &&  ! result ;  ++i)
+	{
+		stringList* const exts = LanguageTable [i]->currentExtensions;
+		if (exts != NULL  &&  stringListRemoveExtension (exts, extension))
+		{
+			verbose (" (removed from %s)", getLanguageName (i));
+			result = TRUE;
+		}
+	}
+	return result;
+}
+
+extern void addLanguageExtensionMap (
+		const langType language, const char* extension)
+{
+	vString* const str = vStringNewInit (extension);
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	removeLanguageExtensionMap (extension);
+	stringListAdd (LanguageTable [language]->currentExtensions, str);
+}
+
+extern void enableLanguage (const langType language, const boolean state)
+{
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	LanguageTable [language]->enabled = state;
+}
+
+extern void enableLanguages (const boolean state)
+{
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  ;  ++i)
+		enableLanguage (i, state);
+}
+
+static void initializeParsers (void)
+{
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  ;  ++i)
+		if (LanguageTable [i]->initialize != NULL)
+			(LanguageTable [i]->initialize) ((langType) i);
+}
+
+extern void initializeParsing (void)
+{
+	unsigned int builtInCount;
+	unsigned int i;
+
+	builtInCount = sizeof (BuiltInParsers) / sizeof (BuiltInParsers [0]);
+	LanguageTable = xMalloc (builtInCount, parserDefinition*);
+
+	verbose ("Installing parsers: ");
+	for (i = 0  ;  i < builtInCount  ;  ++i)
+	{
+		parserDefinition* const def = (*BuiltInParsers [i]) ();
+		if (def != NULL)
+		{
+			boolean accepted = FALSE;
+			if (def->name == NULL  ||  def->name[0] == '\0')
+				error (FATAL, "parser definition must contain name\n");
+			else if (def->regex)
+			{
+#ifdef HAVE_REGEX
+				def->parser = findRegexTags;
+				accepted = TRUE;
+#endif
+			}
+			else if ((def->parser == NULL)  ==  (def->parser2 == NULL))
+				error (FATAL,
+		"%s parser definition must define one and only one parsing routine\n",
+					   def->name);
+			else
+				accepted = TRUE;
+			if (accepted)
+			{
+				verbose ("%s%s", i > 0 ? ", " : "", def->name);
+				def->id = LanguageCount++;
+				LanguageTable [def->id] = def;
+			}
+		}
+	}
+	verbose ("\n");
+	enableLanguages (TRUE);
+	initializeParsers ();
+}
+
+extern void freeParserResources (void)
+{
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  ;  ++i)
+	{
+		parserDefinition* const lang = LanguageTable [i];
+		freeList (&lang->currentPatterns);
+		freeList (&lang->currentExtensions);
+		eFree (lang->name);
+		lang->name = NULL;
+		eFree (lang);
+	}
+	if (LanguageTable != NULL)
+		eFree (LanguageTable);
+	LanguageTable = NULL;
+	LanguageCount = 0;
+}
+
+/*
+*   Option parsing
+*/
+
+extern void processLanguageDefineOption (
+		const char *const option, const char *const parameter)
+{
+#ifdef HAVE_REGEX
+	if (parameter [0] == '\0')
+		error (WARNING, "No language specified for \"%s\" option", option);
+	else if (getNamedLanguage (parameter) != LANG_IGNORE)
+		error (WARNING, "Language \"%s\" already defined", parameter);
+	else
+	{
+		unsigned int i = LanguageCount++;
+		parserDefinition* const def = parserNew (parameter);
+		def->parser            = findRegexTags;
+		def->currentPatterns   = stringListNew ();
+		def->currentExtensions = stringListNew ();
+		def->regex             = TRUE;
+		def->enabled           = TRUE;
+		def->id                = i;
+		LanguageTable = xRealloc (LanguageTable, i + 1, parserDefinition*);
+		LanguageTable [i] = def;
+	}
+#else
+	error (WARNING, "regex support not available; required for --%s option",
+		   option);
+#endif
+}
+
+static kindOption *langKindOption (const langType language, const int flag)
+{
+	unsigned int i;
+	kindOption* result = NULL;
+	const parserDefinition* lang;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	for (i=0  ;  i < lang->kindCount  &&  result == NULL  ;  ++i)
+		if (lang->kinds [i].letter == flag)
+			result = &lang->kinds [i];
+	return result;
+}
+
+static void disableLanguageKinds (const langType language)
+{
+	const parserDefinition* lang;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	if (lang->regex)
+		disableRegexKinds (language);
+	else
+	{
+		unsigned int i;
+		for (i = 0  ;  i < lang->kindCount  ;  ++i)
+			lang->kinds [i].enabled = FALSE;
+	}
+}
+
+static boolean enableLanguageKind (
+		const langType language, const int kind, const boolean mode)
+{
+	boolean result = FALSE;
+	if (LanguageTable [language]->regex)
+		result = enableRegexKind (language, kind, mode);
+	else
+	{
+		kindOption* const opt = langKindOption (language, kind);
+		if (opt != NULL)
+		{
+			opt->enabled = mode;
+			result = TRUE;
+		}
+	}
+	return result;
+}
+
+static void processLangKindOption (
+		const langType language, const char *const option,
+		const char *const parameter)
+{
+	const char *p = parameter;
+	boolean mode = TRUE;
+	int c;
+
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	if (*p != '+'  &&  *p != '-')
+		disableLanguageKinds (language);
+	while ((c = *p++) != '\0') switch (c)
+	{
+		case '+': mode = TRUE;  break;
+		case '-': mode = FALSE; break;
+		default:
+			if (! enableLanguageKind (language, c, mode))
+				error (WARNING, "Unsupported parameter '%c' for --%s option",
+					c, option);
+			break;
+	}
+}
+
+extern boolean processKindOption (
+		const char *const option, const char *const parameter)
+{
+	boolean handled = FALSE;
+	const char* const dash = strchr (option, '-');
+	if (dash != NULL  &&
+		(strcmp (dash + 1, "kinds") == 0  ||  strcmp (dash + 1, "types") == 0))
+	{
+		langType language;
+		vString* langName = vStringNew ();
+		vStringNCopyS (langName, option, dash - option);
+		language = getNamedLanguage (vStringValue (langName));
+		if (language == LANG_IGNORE)
+			error (WARNING, "Unknown language \"%s\" in \"%s\" option", vStringValue (langName), option);
+		else
+			processLangKindOption (language, option, parameter);
+		vStringDelete (langName);
+		handled = TRUE;
+	}
+	return handled;
+}
+
+static void printLanguageKind (const kindOption* const kind, boolean indent)
+{
+	const char *const indentation = indent ? "    " : "";
+	printf ("%s%c  %s%s\n", indentation, kind->letter,
+		kind->description != NULL ? kind->description :
+			(kind->name != NULL ? kind->name : ""),
+		kind->enabled ? "" : " [off]");
+}
+
+static void printKinds (langType language, boolean indent)
+{
+	const parserDefinition* lang;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	if (lang->kinds != NULL  ||  lang->regex)
+	{
+		unsigned int i;
+		for (i = 0  ;  i < lang->kindCount  ;  ++i)
+			printLanguageKind (lang->kinds + i, indent);
+		printRegexKinds (language, indent);
+	}
+}
+
+extern void printLanguageKinds (const langType language)
+{
+	if (language == LANG_AUTO)
+	{
+		unsigned int i;
+		for (i = 0  ;  i < LanguageCount  ;  ++i)
+		{
+			const parserDefinition* const lang = LanguageTable [i];
+			printf ("%s%s\n", lang->name, lang->enabled ? "" : " [disabled]");
+			printKinds (i, TRUE);
+		}
+	}
+	else
+		printKinds (language, FALSE);
+}
+
+static void printMaps (const langType language)
+{
+	const parserDefinition* lang;
+	unsigned int i;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	printf ("%-8s", lang->name);
+	if (lang->currentExtensions != NULL)
+		for (i = 0  ;  i < stringListCount (lang->currentExtensions)  ;  ++i)
+			printf (" *.%s", vStringValue (
+						stringListItem (lang->currentExtensions, i)));
+	if (lang->currentPatterns != NULL)
+		for (i = 0  ;  i < stringListCount (lang->currentPatterns)  ;  ++i)
+			printf (" %s", vStringValue (
+						stringListItem (lang->currentPatterns, i)));
+	putchar ('\n');
+}
+
+extern void printLanguageMaps (const langType language)
+{
+	if (language == LANG_AUTO)
+	{
+		unsigned int i;
+		for (i = 0  ;  i < LanguageCount  ;  ++i)
+			printMaps (i);
+	}
+	else
+		printMaps (language);
+}
+
+static void printLanguage (const langType language)
+{
+	const parserDefinition* lang;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	lang = LanguageTable [language];
+	if (lang->kinds != NULL  ||  lang->regex)
+		printf ("%s%s\n", lang->name, lang->enabled ? "" : " [disabled]");
+}
+	    
+extern void printLanguageList (void)
+{
+	unsigned int i;
+	for (i = 0  ;  i < LanguageCount  ;  ++i)
+		printLanguage (i);
+}
+
+/*
+*   File parsing
+*/
+
+static void makeFileTag (const char *const fileName)
+{
+	if (Option.include.fileNames)
+	{
+		tagEntryInfo tag;
+		initTagEntry (&tag, baseFilename (fileName));
+
+		tag.isFileEntry     = TRUE;
+		tag.lineNumberEntry = TRUE;
+		tag.lineNumber      = 1;
+		tag.kindName        = "file";
+		tag.kind            = 'F';
+
+		makeTagEntry (&tag);
+	}
+}
+
+static boolean createTagsForFile (
+		const char *const fileName, const langType language,
+		const unsigned int passCount)
+{
+	boolean retried = FALSE;
+	Assert (0 <= language  &&  language < (int) LanguageCount);
+	if (fileOpen (fileName, language))
+	{
+		const parserDefinition* const lang = LanguageTable [language];
+		if (Option.etags)
+			beginEtagsFile ();
+
+		makeFileTag (fileName);
+
+		if (lang->parser != NULL)
+			lang->parser ();
+		else if (lang->parser2 != NULL)
+			retried = lang->parser2 (passCount);
+
+		if (Option.etags)
+			endEtagsFile (getSourceFileTagPath ());
+
+		fileClose ();
+	}
+
+	return retried;
+}
+
+static boolean createTagsWithFallback (
+		const char *const fileName, const langType language)
+{
+	const unsigned long numTags	= TagFile.numTags.added;
+	fpos_t tagFilePosition;
+	unsigned int passCount = 0;
+	boolean tagFileResized = FALSE;
+
+	fgetpos (TagFile.fp, &tagFilePosition);
+	while (createTagsForFile (fileName, language, ++passCount))
+	{
+		/*  Restore prior state of tag file.
+		 */
+		fsetpos (TagFile.fp, &tagFilePosition);
+		TagFile.numTags.added = numTags;
+		tagFileResized = TRUE;
+	}
+	return tagFileResized;
+}
+
+extern boolean parseFile (const char *const fileName)
+{
+	boolean tagFileResized = FALSE;
+	langType language = Option.language;
+	if (Option.language == LANG_AUTO)
+		language = getFileLanguage (fileName);
+	Assert (language != LANG_AUTO);
+	if (language == LANG_IGNORE)
+		verbose ("ignoring %s (unknown language)\n", fileName);
+	else if (! LanguageTable [language]->enabled)
+		verbose ("ignoring %s (language disabled)\n", fileName);
+	else
+	{
+		if (Option.filter)
+			openTagFile ();
+
+		tagFileResized = createTagsWithFallback (fileName, language);
+
+		if (Option.filter)
+			closeTagFile (tagFileResized);
+		addTotals (1, 0L, 0L);
+
+		return tagFileResized;
+	}
+	return tagFileResized;
+}
+
+/* vi:set tabstop=4 shiftwidth=4 nowrap: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/python.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/python.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/python.c	(revision 5)
@@ -0,0 +1,771 @@
+/*
+*   $Id: python.c 720 2009-07-07 03:55:23Z dhiebert $
+*
+*   Copyright (c) 2000-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains functions for generating tags for Python language
+*   files.
+*/
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include <string.h>
+
+#include "entry.h"
+#include "options.h"
+#include "read.h"
+#include "main.h"
+#include "vstring.h"
+#include "routines.h"
+#include "debug.h"
+
+/*
+*   DATA DECLARATIONS
+*/
+typedef struct NestingLevel NestingLevel;
+typedef struct NestingLevels NestingLevels;
+
+struct NestingLevel
+{
+	int indentation;
+	vString *name;
+	int type;
+};
+
+struct NestingLevels
+{
+	NestingLevel *levels;
+	int n;					/* number of levels in use */
+	int allocated;
+};
+
+typedef enum {
+	K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
+} pythonKind;
+
+/*
+*   DATA DEFINITIONS
+*/
+static kindOption PythonKinds[] = {
+	{TRUE, 'c', "class",    "classes"},
+	{TRUE, 'f', "function", "functions"},
+	{TRUE, 'm', "member",   "class members"},
+    {TRUE, 'v', "variable", "variables"},
+    {TRUE, 'i', "namespace", "imports"}
+};
+
+static char const * const singletriple = "'''";
+static char const * const doubletriple = "\"\"\"";
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+static NestingLevels *nestingLevelsNew (void)
+{
+	NestingLevels *nls = xCalloc (1, NestingLevels);
+	return nls;
+}
+
+static void nestingLevelsFree (NestingLevels *nls)
+{
+	int i;
+	for (i = 0; i < nls->allocated; i++)
+		vStringDelete(nls->levels[i].name);
+	if (nls->levels) eFree(nls->levels);
+	eFree(nls);
+}
+
+static void nestingLevelsPush (NestingLevels *nls,
+	const vString *name, int type)
+{
+	NestingLevel *nl = NULL;
+
+	if (nls->n >= nls->allocated)
+	{
+		nls->allocated++;
+		nls->levels = xRealloc(nls->levels,
+			nls->allocated, NestingLevel);
+		nls->levels[nls->n].name = vStringNew();
+	}
+	nl = &nls->levels[nls->n];
+	nls->n++;
+
+	vStringCopy(nl->name, name);
+	nl->type = type;
+}
+
+#if 0
+static NestingLevel *nestingLevelsGetCurrent (NestingLevels *nls)
+{
+	Assert (nls != NULL);
+
+	if (nls->n < 1)
+		return NULL;
+
+	return &nls->levels[nls->n - 1];
+}
+
+static void nestingLevelsPop (NestingLevels *nls)
+{
+	const NestingLevel *nl = nestingLevelsGetCurrent(nls);
+
+	Assert (nl != NULL);
+	vStringClear(nl->name);
+	nls->n--;
+}
+#endif
+
+static boolean isIdentifierFirstCharacter (int c)
+{
+	return (boolean) (isalpha (c) || c == '_');
+}
+
+static boolean isIdentifierCharacter (int c)
+{
+	return (boolean) (isalnum (c) || c == '_');
+}
+
+/* Given a string with the contents of a line directly after the "def" keyword,
+ * extract all relevant information and create a tag.
+ */
+static void makeFunctionTag (vString *const function,
+	vString *const parent, int is_class_parent, const char *arglist)
+{
+	tagEntryInfo tag;
+	initTagEntry (&tag, vStringValue (function));
+
+	tag.kindName = "function";
+	tag.kind = 'f';
+	/* tag.extensionFields.arglist = arglist; */
+
+	if (vStringLength (parent) > 0)
+	{
+		if (is_class_parent)
+		{
+			tag.kindName = "member";
+			tag.kind = 'm';
+			tag.extensionFields.scope [0] = "class";
+			tag.extensionFields.scope [1] = vStringValue (parent);
+		}
+		else
+		{
+			tag.extensionFields.scope [0] = "function";
+			tag.extensionFields.scope [1] = vStringValue (parent);
+		}
+	}
+
+	/* If a function starts with __, we mark it as file scope.
+	 * FIXME: What is the proper way to signal such attributes?
+	 * TODO: What does functions/classes starting with _ and __ mean in python?
+	 */
+	if (strncmp (vStringValue (function), "__", 2) == 0 &&
+		strcmp (vStringValue (function), "__init__") != 0)
+	{
+		tag.extensionFields.access = "private";
+		tag.isFileScope = TRUE;
+	}
+	else
+	{
+		tag.extensionFields.access = "public";
+	}
+	makeTagEntry (&tag);
+}
+
+/* Given a string with the contents of the line directly after the "class"
+ * keyword, extract all necessary information and create a tag.
+ */
+static void makeClassTag (vString *const class, vString *const inheritance,
+	vString *const parent, int is_class_parent)
+{
+	tagEntryInfo tag;
+	initTagEntry (&tag, vStringValue (class));
+	tag.kindName = "class";
+	tag.kind = 'c';
+	if (vStringLength (parent) > 0)
+	{
+		if (is_class_parent)
+		{
+			tag.extensionFields.scope [0] = "class";
+			tag.extensionFields.scope [1] = vStringValue (parent);
+		}
+		else
+		{
+			tag.extensionFields.scope [0] = "function";
+			tag.extensionFields.scope [1] = vStringValue (parent);
+		}
+	}
+	tag.extensionFields.inheritance = vStringValue (inheritance);
+	makeTagEntry (&tag);
+}
+
+static void makeVariableTag (vString *const var, vString *const parent)
+{
+	tagEntryInfo tag;
+	initTagEntry (&tag, vStringValue (var));
+	tag.kindName = "variable";
+	tag.kind = 'v';
+	if (vStringLength (parent) > 0)
+	{
+		tag.extensionFields.scope [0] = "class";
+		tag.extensionFields.scope [1] = vStringValue (parent);
+	}
+	makeTagEntry (&tag);
+}
+
+/* Skip a single or double quoted string. */
+static const char *skipString (const char *cp)
+{
+	const char *start = cp;
+	int escaped = 0;
+	for (cp++; *cp; cp++)
+	{
+		if (escaped)
+			escaped--;
+		else if (*cp == '\\')
+			escaped++;
+		else if (*cp == *start)
+			return cp + 1;
+	}
+	return cp;
+}
+
+/* Skip everything up to an identifier start. */
+static const char *skipEverything (const char *cp)
+{
+	for (; *cp; cp++)
+	{
+		if (*cp == '"' || *cp == '\'')
+		{
+			cp = skipString(cp);
+			if (!*cp) break;
+		}
+		if (isIdentifierFirstCharacter ((int) *cp))
+			return cp;
+	}
+	return cp;
+}
+
+/* Skip an identifier. */
+static const char *skipIdentifier (const char *cp)
+{
+	while (isIdentifierCharacter ((int) *cp))
+		cp++;
+	return cp;
+}
+
+static const char *findDefinitionOrClass (const char *cp)
+{
+	while (*cp)
+	{
+		cp = skipEverything (cp);
+		if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5) ||
+			!strncmp(cp, "cdef", 4) || !strncmp(cp, "cpdef", 5))
+		{
+			return cp;
+		}
+		cp = skipIdentifier (cp);
+	}
+	return NULL;
+}
+
+static const char *skipSpace (const char *cp)
+{
+	while (isspace ((int) *cp))
+		++cp;
+	return cp;
+}
+
+/* Starting at ''cp'', parse an identifier into ''identifier''. */
+static const char *parseIdentifier (const char *cp, vString *const identifier)
+{
+	vStringClear (identifier);
+	while (isIdentifierCharacter ((int) *cp))
+	{
+		vStringPut (identifier, (int) *cp);
+		++cp;
+	}
+	vStringTerminate (identifier);
+	return cp;
+}
+
+static void parseClass (const char *cp, vString *const class,
+	vString *const parent, int is_class_parent)
+{
+	vString *const inheritance = vStringNew ();
+	vStringClear (inheritance);
+	cp = parseIdentifier (cp, class);
+	cp = skipSpace (cp);
+	if (*cp == '(')
+	{
+		++cp;
+		while (*cp != ')')
+		{
+			if (*cp == '\0')
+			{
+				/* Closing parenthesis can be in follow up line. */
+				cp = (const char *) fileReadLine ();
+				if (!cp) break;
+				vStringPut (inheritance, ' ');
+				continue;
+			}
+			vStringPut (inheritance, *cp);
+			++cp;
+		}
+		vStringTerminate (inheritance);
+	}
+	makeClassTag (class, inheritance, parent, is_class_parent);
+	vStringDelete (inheritance);
+}
+
+static void parseImports (const char *cp)
+{
+	const char *pos;
+	vString *name, *name_next;
+
+	cp = skipEverything (cp);
+
+	if ((pos = strstr (cp, "import")) == NULL)
+		return;
+
+	cp = pos + 6;
+
+	/* continue only if there is some space between the keyword and the identifier */
+	if (! isspace (*cp))
+		return;
+
+	cp++;
+	cp = skipSpace (cp);
+
+	name = vStringNew ();
+	name_next = vStringNew ();
+
+	cp = skipEverything (cp);
+	while (*cp)
+	{
+		cp = parseIdentifier (cp, name);
+
+		cp = skipEverything (cp);
+		/* we parse the next possible import statement as well to be able to ignore 'foo' in
+		 * 'import foo as bar' */
+		parseIdentifier (cp, name_next);
+
+		/* take the current tag only if the next one is not "as" */
+		if (strcmp (vStringValue (name_next), "as") != 0 &&
+			strcmp (vStringValue (name), "as") != 0)
+		{
+			makeSimpleTag (name, PythonKinds, K_IMPORT);
+		}
+	}
+	vStringDelete (name);
+	vStringDelete (name_next);
+}
+
+/* modified from get.c getArglistFromStr().
+ * warning: terminates rest of string past arglist!
+ * note: does not ignore brackets inside strings! */
+static char *parseArglist(const char *buf)
+{
+	char *start, *end;
+	int level;
+	if (NULL == buf)
+		return NULL;
+	if (NULL == (start = strchr(buf, '(')))
+		return NULL;
+	for (level = 1, end = start + 1; level > 0; ++end)
+	{
+		if ('\0' == *end)
+			break;
+		else if ('(' == *end)
+			++ level;
+		else if (')' == *end)
+			-- level;
+	}
+	*end = '\0';
+	return strdup(start);
+}
+
+static void parseFunction (const char *cp, vString *const def,
+	vString *const parent, int is_class_parent)
+{
+	char *arglist;
+
+	cp = parseIdentifier (cp, def);
+	arglist = parseArglist (cp);
+	makeFunctionTag (def, parent, is_class_parent, arglist);
+	eFree (arglist);
+}
+
+/* Get the combined name of a nested symbol. Classes are separated with ".",
+ * functions with "/". For example this code:
+ * class MyClass:
+ *     def myFunction:
+ *         def SubFunction:
+ *             class SubClass:
+ *                 def Method:
+ *                     pass
+ * Would produce this string:
+ * MyClass.MyFunction/SubFunction/SubClass.Method
+ */
+static boolean constructParentString(NestingLevels *nls, int indent,
+	vString *result)
+{
+	int i;
+	NestingLevel *prev = NULL;
+	int is_class = FALSE;
+	vStringClear (result);
+	for (i = 0; i < nls->n; i++)
+	{
+		NestingLevel *nl = nls->levels + i;
+		if (indent <= nl->indentation)
+			break;
+		if (prev)
+		{
+			vStringCatS(result, ".");	/* make Geany symbol list grouping work properly */
+/*
+			if (prev->type == K_CLASS)
+				vStringCatS(result, ".");
+			else
+				vStringCatS(result, "/");
+*/
+		}
+		vStringCat(result, nl->name);
+		is_class = (nl->type == K_CLASS);
+		prev = nl;
+	}
+	return is_class;
+}
+
+/* Check whether parent's indentation level is higher than the current level and
+ * if so, remove it.
+ */
+static void checkParent(NestingLevels *nls, int indent, vString *parent)
+{
+	int i;
+	NestingLevel *n;
+
+	for (i = 0; i < nls->n; i++)
+	{
+		n = nls->levels + i;
+		/* is there a better way to compare two vStrings? */
+		if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
+		{
+			if (n && indent <= n->indentation)
+			{
+				/* remove this level by clearing its name */
+				vStringClear(n->name);
+			}
+			break;
+		}
+	}
+}
+
+static void addNestingLevel(NestingLevels *nls, int indentation,
+	const vString *name, boolean is_class)
+{
+	int i;
+	NestingLevel *nl = NULL;
+
+	for (i = 0; i < nls->n; i++)
+	{
+		nl = nls->levels + i;
+		if (indentation <= nl->indentation) break;
+	}
+	if (i == nls->n)
+	{
+		nestingLevelsPush(nls, name, 0);
+		nl = nls->levels + i;
+	}
+	else
+	{	/* reuse existing slot */
+		nls->n = i + 1;
+		vStringCopy(nl->name, name);
+	}
+	nl->indentation = indentation;
+	nl->type = is_class ? K_CLASS : !K_CLASS;
+}
+
+/* Return a pointer to the start of the next triple string, or NULL. Store
+ * the kind of triple string in "which" if the return is not NULL.
+ */
+static char const *find_triple_start(char const *string, char const **which)
+{
+	char const *cp = string;
+
+	for (; *cp; cp++)
+	{
+		if (*cp == '"' || *cp == '\'')
+		{
+			if (strncmp(cp, doubletriple, 3) == 0)
+			{
+				*which = doubletriple;
+				return cp;
+			}
+			if (strncmp(cp, singletriple, 3) == 0)
+			{
+				*which = singletriple;
+				return cp;
+			}
+			cp = skipString(cp);
+			if (!*cp) break;
+		}
+	}
+	return NULL;
+}
+
+/* Find the end of a triple string as pointed to by "which", and update "which"
+ * with any other triple strings following in the given string.
+ */
+static void find_triple_end(char const *string, char const **which)
+{
+	char const *s = string;
+	while (1)
+	{
+		/* Check if the string ends in the same line. */
+		s = strstr (s, *which);
+		if (!s) break;
+		s += 3;
+		*which = NULL;
+		/* If yes, check if another one starts in the same line. */
+		s = find_triple_start(s, which);
+		if (!s) break;
+		s += 3;
+	}
+}
+
+static const char *findVariable(const char *line)
+{
+	/* Parse global and class variable names (C.x) from assignment statements.
+	 * Object attributes (obj.x) are ignored.
+	 * Assignment to a tuple 'x, y = 2, 3' not supported.
+	 * TODO: ignore duplicate tags from reassignment statements. */
+	const char *cp, *sp, *eq, *start;
+
+	cp = strstr(line, "=");
+	if (!cp)
+		return NULL;
+	eq = cp + 1;
+	while (*eq)
+	{
+		if (*eq == '=')
+			return NULL;	/* ignore '==' operator and 'x=5,y=6)' function lines */
+		if (*eq == '(' || *eq == '#')
+			break;	/* allow 'x = func(b=2,y=2,' lines and comments at the end of line */
+		eq++;
+	}
+
+	/* go backwards to the start of the line, checking we have valid chars */
+	start = cp - 1;
+	while (start >= line && isspace ((int) *start))
+		--start;
+	while (start >= line && isIdentifierCharacter ((int) *start))
+		--start;
+	if (!isIdentifierFirstCharacter(*(start + 1)))
+		return NULL;
+	sp = start;
+	while (sp >= line && isspace ((int) *sp))
+		--sp;
+	if ((sp + 1) != line)	/* the line isn't a simple variable assignment */
+		return NULL;
+	/* the line is valid, parse the variable name */
+	++start;
+	return start;
+}
+
+/* Skip type declaration that optionally follows a cdef/cpdef */
+static const char *skipTypeDecl (const char *cp, boolean *is_class)
+{
+	const char *lastStart = cp, *ptr = cp;
+	int loopCount = 0;
+	ptr = skipSpace(cp);
+	if (!strncmp("extern", ptr, 6)) {
+		ptr += 6;
+		ptr = skipSpace(ptr);
+		if (!strncmp("from", ptr, 4)) { return NULL; }
+	}
+	if (!strncmp("class", ptr, 5)) {
+		ptr += 5 ;
+		*is_class = TRUE;
+		ptr = skipSpace(ptr);
+		return ptr;
+	}
+	/* limit so that we don't pick off "int item=obj()" */
+	while (*ptr && loopCount++ < 2) {
+		while (*ptr && *ptr != '=' && *ptr != '(' && !isspace(*ptr)) ptr++;
+		if (!*ptr || *ptr == '=') return NULL;
+		if (*ptr == '(') {
+		    return lastStart; /* if we stopped on a '(' we are done */
+		}
+		ptr = skipSpace(ptr);
+		lastStart = ptr;
+		while (*lastStart == '*') lastStart++;  /* cdef int *identifier */
+	}
+	return NULL;
+}
+
+static void findPythonTags (void)
+{
+	vString *const continuation = vStringNew ();
+	vString *const name = vStringNew ();
+	vString *const parent = vStringNew();
+
+	NestingLevels *const nesting_levels = nestingLevelsNew();
+
+	const char *line;
+	int line_skip = 0;
+	char const *longStringLiteral = NULL;
+
+	while ((line = (const char *) fileReadLine ()) != NULL)
+	{
+		const char *cp = line, *candidate;
+		char const *longstring;
+		char const *keyword, *variable;
+		int indent;
+
+		cp = skipSpace (cp);
+
+		if (*cp == '\0')  /* skip blank line */
+			continue;
+
+		/* Skip comment if we are not inside a multi-line string. */
+		if (*cp == '#' && !longStringLiteral)
+			continue;
+
+		/* Deal with line continuation. */
+		if (!line_skip) vStringClear(continuation);
+		vStringCatS(continuation, line);
+		vStringStripTrailing(continuation);
+		if (vStringLast(continuation) == '\\')
+		{
+			vStringChop(continuation);
+			vStringCatS(continuation, " ");
+			line_skip = 1;
+			continue;
+		}
+		cp = line = vStringValue(continuation);
+		cp = skipSpace (cp);
+		indent = cp - line;
+		line_skip = 0;
+
+		checkParent(nesting_levels, indent, parent);
+
+		/* Deal with multiline string ending. */
+		if (longStringLiteral)
+		{
+			find_triple_end(cp, &longStringLiteral);
+			continue;
+		}
+
+		/* Deal with multiline string start. */
+		longstring = find_triple_start(cp, &longStringLiteral);
+		if (longstring)
+		{
+			longstring += 3;
+			find_triple_end(longstring, &longStringLiteral);
+			/* We don't parse for any tags in the rest of the line. */
+			continue;
+		}
+
+		/* Deal with def and class keywords. */
+		keyword = findDefinitionOrClass (cp);
+		if (keyword)
+		{
+			boolean found = FALSE;
+			boolean is_class = FALSE;
+			if (!strncmp (keyword, "def ", 4))
+			{
+				cp = skipSpace (keyword + 3);
+				found = TRUE;
+			}
+			else if (!strncmp (keyword, "class ", 6))
+			{
+				cp = skipSpace (keyword + 5);
+				found = TRUE;
+				is_class = TRUE;
+			}
+			else if (!strncmp (keyword, "cdef ", 5))
+		    {
+		        cp = skipSpace(keyword + 4);
+		        candidate = skipTypeDecl (cp, &is_class);
+		        if (candidate)
+		        {
+		    		found = TRUE;
+		    		cp = candidate;
+		        }
+
+		    }
+    		else if (!strncmp (keyword, "cpdef ", 6))
+		    {
+		        cp = skipSpace(keyword + 5);
+		        candidate = skipTypeDecl (cp, &is_class);
+		        if (candidate)
+		        {
+		    		found = TRUE;
+		    		cp = candidate;
+		        }
+		    }
+
+			if (found)
+			{
+				boolean is_parent_class;
+
+				is_parent_class =
+					constructParentString(nesting_levels, indent, parent);
+
+				if (is_class)
+					parseClass (cp, name, parent, is_parent_class);
+				else
+					parseFunction(cp, name, parent, is_parent_class);
+
+				addNestingLevel(nesting_levels, indent, name, is_class);
+			}
+		}
+		/* Find global and class variables */
+		variable = findVariable(line);
+		if (variable)
+		{
+			const char *start = variable;
+			boolean parent_is_class;
+
+			vStringClear (name);
+			while (isIdentifierCharacter ((int) *start))
+			{
+				vStringPut (name, (int) *start);
+				++start;
+			}
+			vStringTerminate (name);
+
+			parent_is_class = constructParentString(nesting_levels, indent, parent);
+			/* skip variables in methods */
+			if (! parent_is_class && vStringLength(parent) > 0)
+				continue;
+
+			makeVariableTag (name, parent);
+		}
+		/* Find and parse imports */
+		parseImports(line);
+	}
+	/* Clean up all memory we allocated. */
+	vStringDelete (parent);
+	vStringDelete (name);
+	vStringDelete (continuation);
+	nestingLevelsFree (nesting_levels);
+}
+
+extern parserDefinition *PythonParser (void)
+{
+    static const char *const extensions[] = { "py", "pyx", "pxd", "pxi" ,"scons", NULL };
+	parserDefinition *def = parserNew ("Python");
+	def->kinds = PythonKinds;
+	def->kindCount = KIND_COUNT (PythonKinds);
+	def->extensions = extensions;
+	def->parser = findPythonTags;
+	return def;
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new/routines.c
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new/routines.c	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new/routines.c	(revision 5)
@@ -0,0 +1,891 @@
+/*
+*   $Id: routines.c 536 2007-06-02 06:09:00Z elliotth $
+*
+*   Copyright (c) 2002-2003, Darren Hiebert
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License.
+*
+*   This module contains a lose assortment of shared functions.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>  /* to declare malloc (), realloc () */
+#endif
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdio.h>  /* to declare tempnam(), and SEEK_SET (hopefully) */
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>  /* to declar O_RDWR, O_CREAT, O_EXCL */
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>  /* to declare mkstemp () */
+#endif
+
+/*  To declare "struct stat" and stat ().
+ */
+#if defined (HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#else
+# if defined (HAVE_TYPES_H)
+#  include <types.h>
+# endif
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#else
+# ifdef HAVE_STAT_H
+#  include <stat.h>
+# endif
+#endif
+
+#ifdef HAVE_DOS_H
+# include <dos.h>  /* to declare MAXPATH */
+#endif
+#ifdef HAVE_DIRECT_H
+# include <direct.h>  /* to _getcwd */
+#endif
+#ifdef HAVE_DIR_H
+# include <dir.h>  /* to declare findfirst() and findnext() */
+#endif
+#ifdef HAVE_IO_H
+# include <io.h>  /* to declare open() */
+#endif
+#include "debug.h"
+#include "routines.h"
+
+/*
+*   MACROS
+*/
+#ifndef TMPDIR
+# define TMPDIR "/tmp"
+#endif
+
+/*  File type tests.
+ */
+#ifndef S_ISREG
+# if defined (S_IFREG) && ! defined (AMIGA)
+#  define S_ISREG(mode)		((mode) & S_IFREG)
+# else
+#  define S_ISREG(mode)		TRUE  /* assume regular file */
+# endif
+#endif
+
+#ifndef S_ISLNK
+# ifdef S_IFLNK
+#  define S_ISLNK(mode)		(((mode) & S_IFMT) == S_IFLNK)
+# else
+#  define S_ISLNK(mode)		FALSE  /* assume no soft links */
+# endif
+#endif
+
+#ifndef S_ISDIR
+# ifdef S_IFDIR
+#  define S_ISDIR(mode)		(((mode) & S_IFMT) == S_IFDIR)
+# else
+#  define S_ISDIR(mode)		FALSE  /* assume no soft links */
+# endif
+#endif
+
+#ifndef S_IFMT
+# define S_IFMT 0
+#endif
+
+#ifndef S_IXUSR
+# define S_IXUSR 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+
+#ifndef S_IRUSR
+# define S_IRUSR 0400
+#endif
+#ifndef S_IWUSR
+# define S_IWUSR 0200
+#endif
+
+#ifndef S_ISUID
+# define S_ISUID 0
+#endif
+
+/*  Hack for rediculous practice of Microsoft Visual C++.
+ */
+#if defined (WIN32)
+# if defined (_MSC_VER)
+#  define stat    _stat
+#  define getcwd  _getcwd
+#  define currentdrive() (_getdrive() + 'A' - 1)
+#  define PATH_MAX  _MAX_PATH
+# elif defined (__BORLANDC__)
+#  define PATH_MAX  MAXPATH
+#  define currentdrive() (getdisk() + 'A')
+# elif defined (DJGPP)
+#  define currentdrive() (getdisk() + 'A')
+# else
+#  define currentdrive() 'C'
+# endif
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 256
+#endif
+
+/*
+ *  Miscellaneous macros
+ */
+#define selected(var,feature)	(((int)(var) & (int)(feature)) == (int)feature)
+
+/*
+*   DATA DEFINITIONS
+*/
+#if defined (MSDOS_STYLE_PATH)
+const char *const PathDelimiters = ":/\\";
+#elif defined (VMS)
+const char *const PathDelimiters = ":]>";
+#endif
+
+char *CurrentDirectory;
+
+static const char *ExecutableProgram;
+static const char *ExecutableName;
+
+/*
+*   FUNCTION PROTOTYPES
+*/
+#ifdef NEED_PROTO_STAT
+extern int stat (const char *, struct stat *);
+#endif
+#ifdef NEED_PROTO_LSTAT
+extern int lstat (const char *, struct stat *);
+#endif
+#if defined (MSDOS) || defined (WIN32) || defined (VMS) || defined (__EMX__) || defined (AMIGA)
+# define lstat(fn,buf) stat(fn,buf)
+#endif
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+extern void freeRoutineResources (void)
+{
+	if (CurrentDirectory != NULL)
+		eFree (CurrentDirectory);
+}
+
+extern void setExecutableName (const char *const path)
+{
+	ExecutableProgram = path;
+	ExecutableName = baseFilename (path);
+#ifdef VAXC
+{
+	/* remove filetype from executable name */
+	char *p = strrchr (ExecutableName, '.');
+	if (p != NULL)
+		*p = '\0';
+}
+#endif
+}
+
+extern const char *getExecutableName (void)
+{
+	return ExecutableName;
+}
+
+extern const char *getExecutablePath (void)
+{
+	return ExecutableProgram;
+}
+
+extern void error (
+		const errorSelection selection, const char *const format, ...)
+{
+	va_list ap;
+
+	va_start (ap, format);
+	fprintf (errout, "%s: %s", getExecutableName (),
+			selected (selection, WARNING) ? "Warning: " : "");
+	vfprintf (errout, format, ap);
+	if (selected (selection, PERROR))
+#ifdef HAVE_STRERROR
+		fprintf (errout, " : %s", strerror (errno));
+#else
+		perror (" ");
+#endif
+	fputs ("\n", errout);
+	va_end (ap);
+	if (selected (selection, FATAL))
+		exit (1);
+}
+
+/*
+ *  Memory allocation functions
+ */
+
+extern void *eMalloc (const size_t size)
+{
+	void *buffer = malloc (size);
+
+	if (buffer == NULL)
+		error (FATAL, "out of memory");
+
+	return buffer;
+}
+
+extern void *eCalloc (const size_t count, const size_t size)
+{
+	void *buffer = calloc (count, size);
+
+	if (buffer == NULL)
+		error (FATAL, "out of memory");
+
+	return buffer;
+}
+
+extern void *eRealloc (void *const ptr, const size_t size)
+{
+	void *buffer;
+	if (ptr == NULL)
+		buffer = eMalloc (size);
+	else
+	{
+		buffer = realloc (ptr, size);
+		if (buffer == NULL)
+			error (FATAL, "out of memory");
+	}
+	return buffer;
+}
+
+extern void eFree (void *const ptr)
+{
+	Assert (ptr != NULL);
+	free (ptr);
+}
+
+/*
+ *  String manipulation functions
+ */
+
+/*
+ * Compare two strings, ignoring case.
+ * Return 0 for match, < 0 for smaller, > 0 for bigger
+ * Make sure case is folded to uppercase in comparison (like for 'sort -f')
+ * This makes a difference when one of the chars lies between upper and lower
+ * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
+ */
+extern int struppercmp (const char *s1, const char *s2)
+{
+	int result;
+	do
+	{
+		result = toupper ((int) *s1) - toupper ((int) *s2);
+	} while (result == 0  &&  *s1++ != '\0'  &&  *s2++ != '\0');
+	return result;
+}
+
+extern int strnuppercmp (const char *s1, const char *s2, size_t n)
+{
+	int result;
+	do
+	{
+		result = toupper ((int) *s1) - toupper ((int) *s2);
+	} while (result == 0  &&  --n > 0  &&  *s1++ != '\0'  &&  *s2++ != '\0');
+	return result;
+}
+
+#ifndef HAVE_STRSTR
+extern char* strstr (const char *str, const char *substr)
+{
+	const size_t length = strlen (substr);
+	const char *match = NULL;
+	const char *p;
+
+	for (p = str  ;  *p != '\0'  &&  match == NULL  ;  ++p)
+		if (strncmp (p, substr, length) == 0)
+			match = p;
+	return (char*) match;
+}
+#endif
+
+extern char* eStrdup (const char* str)
+{
+	char* result = xMalloc (strlen (str) + 1, char);
+	strcpy (result, str);
+	return result;
+}
+
+extern void toLowerString (char* str)
+{
+	while (*str != '\0')
+	{
+		*str = tolower ((int) *str);
+		++str;
+	}
+}
+
+extern void toUpperString (char* str)
+{
+	while (*str != '\0')
+	{
+		*str = toupper ((int) *str);
+		++str;
+	}
+}
+
+/*  Newly allocated string containing lower case conversion of a string.
+ */
+extern char* newLowerString (const char* str)
+{
+	char* const result = xMalloc (strlen (str) + 1, char);
+	int i = 0;
+	do
+		result [i] = tolower ((int) str [i]);
+	while (str [i++] != '\0');
+	return result;
+}
+
+/*  Newly allocated string containing upper case conversion of a string.
+ */
+extern char* newUpperString (const char* str)
+{
+	char* const result = xMalloc (strlen (str) + 1, char);
+	int i = 0;
+	do
+		result [i] = toupper ((int) str [i]);
+	while (str [i++] != '\0');
+	return result;
+}
+
+/*
+ * File system functions
+ */
+
+extern void setCurrentDirectory (void)
+{
+#ifndef AMIGA
+	char* buf;
+#endif
+	if (CurrentDirectory == NULL)
+		CurrentDirectory = xMalloc ((size_t) (PATH_MAX + 1), char);
+#ifdef AMIGA
+	strcpy (CurrentDirectory, ".");
+#else
+	buf = getcwd (CurrentDirectory, PATH_MAX);
+	if (buf == NULL)
+		perror ("");
+#endif
+	if (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1] !=
+			PATH_SEPARATOR)
+	{
+		sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c",
+				OUTPUT_PATH_SEPARATOR);
+	}
+}
+
+#ifdef AMIGA
+static boolean isAmigaDirectory (const char *const name)
+{
+	boolean result = FALSE;
+	struct FileInfoBlock *const fib = xMalloc (1, struct FileInfoBlock);
+	if (fib != NULL)
+	{
+		const BPTR flock = Lock ((UBYTE *) name, (long) ACCESS_READ);
+
+		if (flock != (BPTR) NULL)
+		{
+			if (Examine (flock, fib))
+				result = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
+			UnLock (flock);
+		}
+		eFree (fib);
+	}
+	return result;
+}
+#endif
+
+/* For caching of stat() calls */
+extern fileStatus *eStat (const char *const fileName)
+{
+	struct stat status;
+	static fileStatus file;
+	if (file.name == NULL  ||  strcmp (fileName, file.name) != 0)
+	{
+		eStatFree (&file);
+		file.name = eStrdup (fileName);
+		if (lstat (file.name, &status) != 0)
+			file.exists = FALSE;
+		else
+		{
+			file.isSymbolicLink = (boolean) S_ISLNK (status.st_mode);
+			if (file.isSymbolicLink  &&  stat (file.name, &status) != 0)
+				file.exists = FALSE;
+			else
+			{
+				file.exists = TRUE;
+#ifdef AMIGA
+				file.isDirectory = isAmigaDirectory (file.name);
+#else
+				file.isDirectory = (boolean) S_ISDIR (status.st_mode);
+#endif
+				file.isNormalFile = (boolean) (S_ISREG (status.st_mode));
+				file.isExecutable = (boolean) ((status.st_mode &
+					(S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
+				file.isSetuid = (boolean) ((status.st_mode & S_ISUID) != 0);
+				file.size = status.st_size;
+			}
+		}
+	}
+	return &file;
+}
+
+extern void eStatFree (fileStatus *status)
+{
+	if (status->name != NULL)
+	{
+		eFree (status->name);
+		status->name = NULL;
+	}
+}
+
+extern boolean doesFileExist (const char *const fileName)
+{
+	fileStatus *status = eStat (fileName);
+	return status->exists;
+}
+
+extern boolean isRecursiveLink (const char* const dirName)
+{
+	boolean result = FALSE;
+	fileStatus *status = eStat (dirName);
+	if (status->isSymbolicLink)
+	{
+		char* const path = absoluteFilename (dirName);
+		while (path [strlen (path) - 1] == PATH_SEPARATOR)
+			path [strlen (path) - 1] = '\0';
+		while (! result  &&  strlen (path) > (size_t) 1)
+		{
+			char *const separator = strrchr (path, PATH_SEPARATOR);
+			if (separator == NULL)
+				break;
+			else if (separator == path)  /* backed up to root directory */
+				*(separator + 1) = '\0';
+			else
+				*separator = '\0';
+			result = isSameFile (path, dirName);
+		}
+		eFree (path);
+	}
+	return result;
+}
+
+#ifndef HAVE_FGETPOS
+
+extern int fgetpos (FILE *stream, fpos_t *pos)
+{
+	int result = 0;
+
+	*pos = ftell (stream);
+	if (*pos == -1L)
+		result = -1;
+
+	return result;
+}
+
+extern int fsetpos (FILE *stream, fpos_t const *pos)
+{
+	return fseek (stream, *pos, SEEK_SET);
+}
+
+#endif
+
+/*
+ *  Pathname manipulation (O/S dependent!!!)
+ */
+
+static boolean isPathSeparator (const int c)
+{
+	boolean result;
+#if defined (MSDOS_STYLE_PATH) || defined (VMS)
+	result = (boolean) (strchr (PathDelimiters, c) != NULL);
+#else
+	result = (boolean) (c == PATH_SEPARATOR);
+#endif
+	return result;
+}
+
+#if ! defined (HAVE_STAT_ST_INO)
+
+static void canonicalizePath (char *const path)
+{
+#if defined (MSDOS_STYLE_PATH)
+	char *p;
+	for (p = path  ;  *p != '\0'  ;  ++p)
+		if (isPathSeparator (*p)  &&  *p != ':')
+			*p = PATH_SEPARATOR;
+#endif
+}
+
+#endif
+
+extern boolean isSameFile (const char *const name1, const char *const name2)
+{
+	boolean result = FALSE;
+#if defined (HAVE_STAT_ST_INO)
+	struct stat stat1, stat2;
+
+	if (stat (name1, &stat1) == 0  &&  stat (name2, &stat2) == 0)
+		result = (boolean) (stat1.st_ino == stat2.st_ino);
+#else
+	{
+		char *const n1 = absoluteFilename (name1);
+		char *const n2 = absoluteFilename (name2);
+		canonicalizePath (n1);
+		canonicalizePath (n2);
+# if defined (CASE_INSENSITIVE_FILENAMES)
+		result = (boolean) (strcasecmp (n1, n2) == 0);
+#else
+		result = (boolean) (strcmp (n1, n2) == 0);
+#endif
+		free (n1);
+		free (n2);
+	}
+#endif
+	return result;
+}
+
+extern const char *baseFilename (const char *const filePath)
+{
+#if defined (MSDOS_STYLE_PATH) || defined (VMS)
+	const char *tail = NULL;
+	unsigned int i;
+
+	/*  Find whichever of the path delimiters is last.
+	 */
+	for (i = 0  ;  i < strlen (PathDelimiters)  ;  ++i)
+	{
+		const char *sep = strrchr (filePath, PathDelimiters [i]);
+
+		if (sep > tail)
+			tail = sep;
+	}
+#else
+	const char *tail = strrchr (filePath, PATH_SEPARATOR);
+#endif
+	if (tail == NULL)
+		tail = filePath;
+	else
+		++tail;  /* step past last delimiter */
+#ifdef VAXC
+	{
+		/* remove version number from filename */
+		char *p = strrchr ((char *) tail, ';');
+		if (p != NULL)
+			*p = '\0';
+	}
+#endif
+
+	return tail;
+}
+
+extern const char *fileExtension (const char *const fileName)
+{
+	const char *extension;
+	const char *pDelimiter = NULL;
+	const char *const base = baseFilename (fileName);
+#ifdef QDOS
+	pDelimiter = strrchr (base, '_');
+#endif
+	if (pDelimiter == NULL)
+	    pDelimiter = strrchr (base, '.');
+
+	if (pDelimiter == NULL)
+		extension = "";
+	else
+		extension = pDelimiter + 1;  /* skip to first char of extension */
+
+	return extension;
+}
+
+extern boolean isAbsolutePath (const char *const path)
+{
+	boolean result = FALSE;
+#if defined (MSDOS_STYLE_PATH)
+	if (isPathSeparator (path [0]))
+		result = TRUE;
+	else if (isalpha (path [0])  &&  path [1] == ':')
+	{
+		if (isPathSeparator (path [2]))
+			result = TRUE;
+		else
+			/*  We don't support non-absolute file names with a drive
+			 *  letter, like `d:NAME' (it's too much hassle).
+			 */
+			error (FATAL,
+				"%s: relative file names with drive letters not supported",
+				path);
+	}
+#elif defined (VMS)
+	result = (boolean) (strchr (path, ':') != NULL);
+#else
+	result = isPathSeparator (path [0]);
+#endif
+	return result;
+}
+
+extern vString *combinePathAndFile (
+	const char *const path, const char *const file)
+{
+	vString *const filePath = vStringNew ();
+#ifdef VMS
+	const char *const directoryId = strstr (file, ".DIR;1");
+
+	if (directoryId == NULL)
+	{
+		const char *const versionId = strchr (file, ';');
+
+		vStringCopyS (filePath, path);
+		if (versionId == NULL)
+			vStringCatS (filePath, file);
+		else
+			vStringNCatS (filePath, file, versionId - file);
+		vStringCopyToLower (filePath, filePath);
+	}
+	else
+	{
+		/*  File really is a directory; append it to the path.
+		 *  Gotcha: doesn't work with logical names.
+		 */
+		vStringNCopyS (filePath, path, strlen (path) - 1);
+		vStringPut (filePath, '.');
+		vStringNCatS (filePath, file, directoryId - file);
+		if (strchr (path, '[') != NULL)
+			vStringPut (filePath, ']');
+		else
+			vStringPut (filePath, '>');
+		vStringTerminate (filePath);
+	}
+#else
+	const int lastChar = path [strlen (path) - 1];
+	boolean terminated = isPathSeparator (lastChar);
+
+	vStringCopyS (filePath, path);
+	if (! terminated)
+	{
+		vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
+		vStringTerminate (filePath);
+	}
+	vStringCatS (filePath, file);
+#endif
+
+	return filePath;
+}
+
+/* Return a newly-allocated string whose contents concatenate those of
+ * s1, s2, s3.
+ * Routine adapted from Gnu etags.
+ */
+static char* concat (const char *s1, const char *s2, const char *s3)
+{
+  int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+  char *result = xMalloc (len1 + len2 + len3 + 1, char);
+
+  strcpy (result, s1);
+  strcpy (result + len1, s2);
+  strcpy (result + len1 + len2, s3);
+  result [len1 + len2 + len3] = '\0';
+
+  return result;
+}
+
+/* Return a newly allocated string containing the absolute file name of FILE
+ * given CWD (which should end with a slash).
+ * Routine adapted from Gnu etags.
+ */
+extern char* absoluteFilename (const char *file)
+{
+	char *slashp, *cp;
+	char *res = NULL;
+	if (isAbsolutePath (file))
+	{
+#ifdef MSDOS_STYLE_PATH
+		if (file [1] == ':')
+			res = eStrdup (file);
+		else
+		{
+			char drive [3];
+			sprintf (drive, "%c:", currentdrive ());
+			res = concat (drive, file, "");
+		}
+#else
+		res = eStrdup (file);
+#endif
+	}
+	else
+		res = concat (CurrentDirectory, file, "");
+
+	/* Delete the "/dirname/.." and "/." substrings. */
+	slashp = strchr (res, PATH_SEPARATOR);
+	while (slashp != NULL  &&  slashp [0] != '\0')
+	{
+		if (slashp[1] == '.')
+		{
+			if (slashp [2] == '.' &&
+				(slashp [3] == PATH_SEPARATOR || slashp [3] == '\0'))
+			{
+				cp = slashp;
+				do
+					cp--;
+				while (cp >= res  &&  ! isAbsolutePath (cp));
+				if (cp < res)
+					cp = slashp;/* the absolute name begins with "/.." */
+#ifdef MSDOS_STYLE_PATH
+				/* Under MSDOS and NT we get `d:/NAME' as absolute file name,
+				 * so the luser could say `d:/../NAME'. We silently treat this
+				 * as `d:/NAME'.
+				 */
+				else if (cp [0] != PATH_SEPARATOR)
+					cp = slashp;
+#endif
+				strcpy (cp, slashp + 3);
+				slashp = cp;
+				continue;
+			}
+			else if (slashp [2] == PATH_SEPARATOR  ||  slashp [2] == '\0')
+			{
+				strcpy (slashp, slashp + 2);
+				continue;
+			}
+		}
+		slashp = strchr (slashp + 1, PATH_SEPARATOR);
+	}
+
+	if (res [0] == '\0')
+		return eStrdup ("/");
+	else
+	{
+#ifdef MSDOS_STYLE_PATH
+		/* Canonicalize drive letter case. */
+		if (res [1] == ':'  &&  islower (res [0]))
+			res [0] = toupper (res [0]);
+#endif
+
+		return res;
+	}
+}
+
+/* Return a newly allocated string containing the absolute file name of dir
+ * where `file' resides given `CurrentDirectory'.
+ * Routine adapted from Gnu etags.
+ */
+extern char* absoluteDirname (char *file)
+{
+	char *slashp, *res;
+	char save;
+	slashp = strrchr (file, PATH_SEPARATOR);
+	if (slashp == NULL)
+		res = eStrdup (CurrentDirectory);
+	else
+	{
+		save = slashp [1];
+		slashp [1] = '\0';
+		res = absoluteFilename (file);
+		slashp [1] = save;
+	}
+	return res;
+}
+
+/* Return a newly allocated string containing the file name of FILE relative
+ * to the absolute directory DIR (which should end with a slash).
+ * Routine adapted from Gnu etags.
+ */
+extern char* relativeFilename (const char *file, const char *dir)
+{
+	const char *fp, *dp;
+	char *absdir, *res;
+	int i;
+
+	/* Find the common root of file and dir (with a trailing slash). */
+	absdir = absoluteFilename (file);
+	fp = absdir;
+	dp = dir;
+	while (*fp++ == *dp++)
+		continue;
+	fp--;
+	dp--;  /* back to the first differing char */
+	do
+	{  /* look at the equal chars until path sep */
+		if (fp == absdir)
+			return absdir;  /* first char differs, give up */
+		fp--;
+		dp--;
+	} while (*fp != PATH_SEPARATOR);
+
+	/* Build a sequence of "../" strings for the resulting relative file name.
+	 */
+	i = 0;
+	while ((dp = strchr (dp + 1, PATH_SEPARATOR)) != NULL)
+		i += 1;
+	res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
+	res [0] = '\0';
+	while (i-- > 0)
+		strcat (res, "../");
+
+	/* Add the file name relative to the common root of file and dir. */
+	strcat (res, fp + 1);
+	free (absdir);
+
+	return res;
+}
+
+extern FILE *tempFile (const char *const mode, char **const pName)
+{
+	char *name;
+	FILE *fp;
+	int fd;
+#if defined(HAVE_MKSTEMP)
+	const char *const pattern = "tags.XXXXXX";
+	const char *tmpdir = NULL;
+	fileStatus *file = eStat (ExecutableProgram);
+	if (! file->isSetuid)
+		tmpdir = getenv ("TMPDIR");
+	if (tmpdir == NULL)
+		tmpdir = TMPDIR;
+	name = xMalloc (strlen (tmpdir) + 1 + strlen (pattern) + 1, char);
+	sprintf (name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, pattern);
+	fd = mkstemp (name);
+	eStatFree (file);
+#elif defined(HAVE_TEMPNAM)
+	name = tempnam (TMPDIR, "tags");
+	if (name == NULL)
+		error (FATAL | PERROR, "cannot allocate temporary file name");
+	fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+#else
+	name = xMalloc (L_tmpnam, char);
+	if (tmpnam (name) != name)
+		error (FATAL | PERROR, "cannot assign temporary file name");
+	fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+#endif
+	if (fd == -1)
+		error (FATAL | PERROR, "cannot open temporary file");
+	fp = fdopen (fd, mode);
+	if (fp == NULL)
+		error (FATAL | PERROR, "cannot open temporary file");
+	DebugStatement (
+		debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
+	Assert (*pName == NULL);
+	*pName = name;
+	return fp;
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
Index: create-5.8-unused-attribute-patch/ctags-5.8-new
===================================================================
--- create-5.8-unused-attribute-patch/ctags-5.8-new	(nonexistent)
+++ create-5.8-unused-attribute-patch/ctags-5.8-new	(revision 5)

Property changes on: create-5.8-unused-attribute-patch/ctags-5.8-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-5.8-unused-attribute-patch/file.list
===================================================================
--- create-5.8-unused-attribute-patch/file.list	(nonexistent)
+++ create-5.8-unused-attribute-patch/file.list	(revision 5)
@@ -0,0 +1,10 @@
+ctags-5.8/c.c
+ctags-5.8/eiffel.c
+ctags-5.8/general.h
+ctags-5.8/lregex.c
+ctags-5.8/lua.c
+ctags-5.8/main.c
+ctags-5.8/options.c
+ctags-5.8/parse.c
+ctags-5.8/python.c
+ctags-5.8/routines.c
Index: create-5.8-unused-attribute-patch
===================================================================
--- create-5.8-unused-attribute-patch	(nonexistent)
+++ create-5.8-unused-attribute-patch	(revision 5)

Property changes on: create-5.8-unused-attribute-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: patches/README
===================================================================
--- patches/README	(nonexistent)
+++ patches/README	(revision 5)
@@ -0,0 +1,6 @@
+
+/* begin *
+
+   TODO: Leave some comment here.
+
+ * end */
Index: patches
===================================================================
--- patches	(nonexistent)
+++ patches	(revision 5)

Property changes on: patches
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- .	(nonexistent)
+++ .	(revision 5)

Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~