[geany/geany] 38c365: Update to the upstream latex parser

Jiří Techet git-noreply at geany.org
Thu May 12 23:04:38 UTC 2022


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Thu, 12 May 2022 23:04:38 UTC
Commit:      38c3656d1c25c9ab79ead94160641c3e95075804
             https://github.com/geany/geany/commit/38c3656d1c25c9ab79ead94160641c3e95075804

Log Message:
-----------
Update to the upstream latex parser

The new parser reports scope so update the corresponding functions.

In addition this patch adds new root "Part" for reporting parts and
adds a separate "Bibitem" root (bibitem and label items used to be
reported together which I think is a bit confusing).


Modified Paths:
--------------
    ctags/Makefile.am
    ctags/parsers/geany_tex.c
    ctags/parsers/tex.c
    ctags/parsers/tex.h
    meson.build
    src/tagmanager/tm_parser.c
    tests/ctags/3526726.tex.tags
    tests/ctags/bug2886870.tex.tags
    tests/ctags/intro.tex.tags
    tests/ctags/intro_orig.tex
    tests/ctags/intro_orig.tex.tags

Modified: ctags/Makefile.am
3 lines changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -93,7 +93,8 @@ parsers = \
 	parsers/tcl.c \
 	parsers/tcl.h \
 	parsers/tcloo.c \
-	parsers/geany_tex.c \
+	parsers/tex.c \
+	parsers/tex.h \
 	parsers/txt2tags.c \
 	parsers/verilog.c \
 	parsers/vhdl.c


Modified: ctags/parsers/geany_tex.c
242 lines changed, 0 insertions(+), 242 deletions(-)
===================================================================
@@ -1,242 +0,0 @@
-/*
- *   Copyright (c) 2000-2001, Jérôme Plût
- *   Copyright (c) 2006, Enrico Tröger
- *
- *   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 source files
- *   for the TeX formatting system.
- */
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"    /* must always come first */
-
-#include <ctype.h>
-#include <string.h>
-
-#include "parse.h"
-#include "read.h"
-#include "vstring.h"
-#include "routines.h"
-
-/*
-*   DATA DEFINITIONS
-*/
-typedef enum {
-	K_COMMAND,
-	K_ENVIRONMENT,
-	K_SECTION,
-	K_SUBSECTION,
-	K_SUBSUBSECTION,
-	K_CHAPTER,
-	K_LABEL
-} TeXKind;
-
-static kindDefinition TeXKinds[] = {
-	{ true, 'f', "function",      "command definitions" },
-	{ true, 'c', "class",         "environment definitions" },
-	{ true, 'm', "member",        "labels, sections and bibliography" },
-	{ true, 'd', "macro",         "subsections" },
-	{ true, 'v', "variable",      "subsubsections" },
-	{ true, 'n', "namespace",     "chapters"},
-	{ true, 's', "struct",        "labels and bibliography" }
-};
-
-#define TEX_BRACES (1<<0)
-#define TEX_BSLASH (1<<1)
-#define TEX_LABEL  (1<<2)
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-static int getWord(const char * ref, const char **ptr)
-{
-	const char *p = *ptr;
-
-	while ((*ref != '\0') && (*p != '\0') && (*ref == *p))
-		ref++, p++;
-
-
-	if (*ref)
-		return false;
-
-	if (*p == '*') /* to allow something like \section*{foobar} */
-		p++;
-
-	*ptr = p;
-	return true;
-}
-
-static void createTag(int flags, TeXKind kind, const char * l)
-{
-	vString *name = vStringNew ();
-
-	while ((*l == ' '))
-		l++;
-	if (flags & (TEX_BRACES | TEX_LABEL))
-	{
-		if (*l == '[')
-		{
-			while (*l != ']')
-			{
-				if (*l == '\0')
-					goto no_tag;
-				l++;
-			}
-			l++; /* skip the closing square bracket */
-		}
-		if (*l != '{')
-			goto no_tag;
-		l++;
-	}
-	if (flags & TEX_BSLASH)
-	{
-		if ((*(l++)) != '\\')
-			goto no_tag;
-	}
-	if (flags & TEX_LABEL)
-	{
-		do
-		{
-			vStringPut(name, (int) *l);
-			++l;
-		} while ((*l != '\0') && (*l != '}'));
-		if (name->buffer[0] != '}')
-			makeSimpleTag(name, kind);
-	}
-	else if (isalpha((int) *l) || *l == '@')
-	{
-		do
-		{
-			vStringPut (name, (int) *l);
-			++l;
-		} while (isalpha((int) *l) || *l == '@');
-		makeSimpleTag(name, kind);
-	}
-	else
-	{
-		vStringPut(name, (int) *l);
-		makeSimpleTag(name, kind);
-	}
-
-no_tag:
-	vStringDelete(name);
-}
-
-static void findTeXTags(void)
-{
-	const char *line;
-
-	while ((line = (const char*)readLineFromInputFile()) != NULL)
-	{
-		const char *cp = line;
-		/*int escaped = 0;*/
-
-		for (; *cp != '\0'; cp++)
-		{
-			if (*cp == '%')
-				break;
-			if (*cp == '\\')
-			{
-				cp++;
-
-				/* \newcommand{\command} */
-				if (getWord("newcommand", &cp)
-					|| getWord("providecommand", &cp)
-					|| getWord("renewcommand", &cp)
-					)
-				{
-					createTag (TEX_BRACES|TEX_BSLASH, K_COMMAND, cp);
-					continue;
-				}
-
-				/* \DeclareMathOperator{\command} */
-				else if (getWord("DeclareMathOperator", &cp))
-				{
-					if (*cp == '*')
-						cp++;
-					createTag(TEX_BRACES|TEX_BSLASH, K_COMMAND, cp);
-					continue;
-				}
-
-				/* \def\command */
-				else if (getWord("def", &cp))
-				{
-					createTag(TEX_BSLASH, K_COMMAND, cp);
-					continue;
-				}
-
-				/* \newenvironment{name} */
-				else if ( getWord("newenvironment", &cp)
-					|| getWord("newtheorem", &cp)
-					|| getWord("begin", &cp)
-					)
-				{
-					createTag(TEX_BRACES, K_ENVIRONMENT, cp);
-					continue;
-				}
-
-				/* \bibitem[label]{key} */
-				else if (getWord("bibitem", &cp))
-				{
-					while (*cp == ' ')
-						cp++;
-					if (*(cp++) != '[')
-						break;
-					while ((*cp != '\0') && (*cp != ']'))
-						cp++;
-					if (*(cp++) != ']')
-						break;
-					createTag(TEX_LABEL, K_LABEL, cp);
-					continue;
-				}
-
-				/* \label{key} */
-				else if (getWord("label", &cp))
-				{
-					createTag(TEX_LABEL, K_LABEL, cp);
-					continue;
-				}
-				/* \section{key} */
-				else if (getWord("section", &cp))
-				{
-					createTag(TEX_LABEL, K_SECTION, cp);
-					continue;
-				}
-				/* \subsection{key} */
-				else if (getWord("subsection", &cp))
-				{
-					createTag(TEX_LABEL, K_SUBSECTION, cp);
-					continue;
-				}
-				/* \subsubsection{key} */
-				else if (getWord("subsubsection", &cp))
-				{
-					createTag(TEX_LABEL, K_SUBSUBSECTION, cp);
-					continue;
-				}
-				/* \chapter{key} */
-				else if (getWord("chapter", &cp))
-				{
-					createTag(TEX_LABEL, K_CHAPTER, cp);
-					continue;
-				}
-			}
-		}
-	}
-}
-
-extern parserDefinition* TexParser (void)
-{
-	static const char *const extensions [] = { "tex", "sty", "idx", NULL };
-	parserDefinition * def = parserNew ("LaTeX");
-	def->kindTable  = TeXKinds;
-	def->kindCount  = ARRAY_SIZE (TeXKinds);
-	def->extensions = extensions;
-	def->parser     = findTeXTags;
-	return def;
-}


Modified: ctags/parsers/tex.c
1253 lines changed, 1253 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,1253 @@
+/*
+ *	 Copyright (c) 2008, David Fishburn
+ *	 Copyright (c) 2012, Jan Larres
+ *
+ *	 This source code is released for free distribution under the terms of the
+ *	 GNU General Public License version 2 or (at your option) any later version.
+ *
+ *	 This module contains functions for generating tags for TeX language files.
+ *
+ *	 Tex language reference:
+ *		 http://en.wikibooks.org/wiki/TeX#The_Structure_of_TeX
+ */
+
+/*
+ *	 INCLUDE FILES
+ */
+#include "general.h"	/* must always come first */
+#include <ctype.h>	/* to define isalpha () */
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+#include <string.h>
+
+#include "debug.h"
+#include "entry.h"
+#include "keyword.h"
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+#include "vstring.h"
+
+#include "tex.h"
+
+/*
+ *	 MACROS
+ */
+#define isType(token,t)		(bool) ((token)->type == (t))
+#define isKeyword(token,k)	(bool) ((token)->keyword == (k))
+#define isIdentChar(c) \
+	(isalpha (c) || isdigit (c) || (c) >= 0x80 || (c) == '$' || \
+		(c) == '_' || (c) == '#' || (c) == '-' || (c) == '.' || (c) == ':')
+
+/*
+ *	 DATA DECLARATIONS
+ */
+
+/*
+ * Used to specify type of keyword.
+ */
+enum eKeywordId {
+	KEYWORD_part,
+	KEYWORD_chapter,
+	KEYWORD_section,
+	KEYWORD_subsection,
+	KEYWORD_subsubsection,
+	KEYWORD_paragraph,
+	KEYWORD_subparagraph,
+	KEYWORD_label,
+	KEYWORD_include,
+	KEYWORD_input,
+	KEYWORD_begin,
+	KEYWORD_end,
+	KEYWORD_bibitem,
+	KEYWORD_bibliography,
+	KEYWORD_newcommand,
+	KEYWORD_renewcommand,
+	KEYWORD_providecommand,
+	KEYWORD_def,
+	KEYWORD_declaremathoperator,
+	KEYWORD_newenvironment,
+	KEYWORD_renewenvironment,
+	KEYWORD_newtheorem,
+	KEYWORD_newcounter,
+};
+typedef int keywordId; /* to allow KEYWORD_NONE */
+
+enum eTokenType {
+	/* 0..255 are the byte's value.  Some are named for convenience */
+	TOKEN_OPEN_PAREN = '(',
+	TOKEN_CLOSE_PAREN = ')',
+	TOKEN_OPEN_CURLY = '{',
+	TOKEN_CLOSE_CURLY = '}',
+	TOKEN_OPEN_SQUARE = '[',
+	TOKEN_CLOSE_SQUARE = ']',
+	TOKEN_STAR = '*',
+	/* above is special types */
+	TOKEN_UNDEFINED = 256,
+	TOKEN_KEYWORD,
+	TOKEN_IDENTIFIER,
+	TOKEN_STRING,
+};
+typedef int tokenType;
+
+typedef struct sTokenInfo {
+	tokenType		type;
+	keywordId		keyword;
+	vString *		string;
+	vString *		scope;
+	unsigned long 	lineNumber;
+	MIOPos 			filePosition;
+} tokenInfo;
+
+/*
+ *	DATA DEFINITIONS
+ */
+
+static langType Lang_tex;
+
+static vString *lastPart;
+static vString *lastChapter;
+static vString *lastSection;
+static vString *lastSubS;
+static vString *lastSubSubS;
+
+typedef enum {
+	TEXTAG_PART,
+	TEXTAG_CHAPTER,
+	TEXTAG_SECTION,
+	TEXTAG_SUBSECTION,
+	TEXTAG_SUBSUBSECTION,
+	TEXTAG_PARAGRAPH,
+	TEXTAG_SUBPARAGRAPH,
+	TEXTAG_LABEL,
+	TEXTAG_XINPUT,
+	TEXTAG_BIBITEM,
+	TEXTAG_COMMAND,
+	TEXTAG_OPERATOR,
+	TEXTAG_ENVIRONMENT,
+	TEXTAG_THEOREM,
+	TEXTAG_COUNTER,
+	TEXTAG_COUNT
+} texKind;
+
+typedef enum {
+	TEX_XINPUT_INCLUDED,
+	TEX_XINPUT_INPUT,
+	TEX_XINPUT_BIBLIOGRAPHY,
+} texInputRole;
+
+typedef enum {
+	TEX_ENVIRONMENT_USED,
+} texEnvironmentRole;
+
+static roleDefinition TexInputRoles [] = {
+	{ true, "included",
+	  "external input file specified with \\include" },
+	{ true, "input",
+	  "external input file specified with \\input" },
+	{ true, "bibliography",
+	  "bibliography (.bib) file" },
+};
+
+static roleDefinition TexEnvironmentRoles [] = {
+	{ false, "used", "environment usage introduced by \\begin{MyEnv}" },
+};
+
+static kindDefinition TexKinds [] = {
+	{ true,  'p', "part",			  "parts"			   },
+	{ true,  'c', "chapter",		  "chapters"		   },
+	{ true,  's', "section",		  "sections"		   },
+	{ true,  'u', "subsection",		  "subsections"		   },
+	{ true,  'b', "subsubsection",	  "subsubsections"	   },
+	{ true,  'P', "paragraph",		  "paragraphs"		   },
+	{ true,  'G', "subparagraph",	  "subparagraphs"	   },
+	{ true,  'l', "label",			  "labels"			   },
+	{ true,  'i', "xinput",			  "external input files",
+	  .referenceOnly = true, ATTACH_ROLES(TexInputRoles)   },
+	{ true,  'B', "bibitem",		  "bibliography items" },
+	{ true,  'C', "command",		  "command created with \\newcommand" },
+	{ true,  'o', "operator",		  "math operator created with \\DeclareMathOperator" },
+	{ true,  'e', "environment",	  "environment created with \\newenvironment",
+	  .referenceOnly = false, ATTACH_ROLES(TexEnvironmentRoles) },
+	{ true,  't', "theorem",		  "theorem created with \\newtheorem" },
+	{ true,  'N', "counter",		  "counter created with \\newcounter" },
+};
+
+static const keywordTable TexKeywordTable [] = {
+	/* keyword			keyword ID */
+	{ "part",			KEYWORD_part				},
+	{ "chapter",		KEYWORD_chapter				},
+	{ "section",		KEYWORD_section				},
+	{ "subsection",		KEYWORD_subsection			},
+	{ "subsubsection",	KEYWORD_subsubsection		},
+	{ "paragraph",		KEYWORD_paragraph			},
+	{ "subparagraph",	KEYWORD_subparagraph		},
+	{ "label",			KEYWORD_label				},
+	{ "include",		KEYWORD_include				},
+	{ "input",			KEYWORD_input				},
+	{ "begin",			KEYWORD_begin				},
+	{ "end",			KEYWORD_end					},
+	{ "bibitem",		KEYWORD_bibitem				},
+	{ "bibliography",	KEYWORD_bibliography		},
+	{ "newcommand",		KEYWORD_newcommand			},
+	{ "renewcommand",	KEYWORD_renewcommand		},
+	{ "providecommand",	KEYWORD_providecommand		},
+	{ "def",			KEYWORD_def					},
+	{ "DeclareMathOperator",	KEYWORD_declaremathoperator	},
+	{ "newenvironment",	KEYWORD_newenvironment		},
+	{ "renewenvironment",	KEYWORD_renewenvironment},
+	{ "newtheorem",		KEYWORD_newtheorem			},
+	{ "newcounter",		KEYWORD_newcounter			},
+};
+
+/*
+ * FUNCTION DECLARATIONS
+ */
+
+static bool notifyReadingIdentifier  (tokenInfo *id_token, bool *tokenUnprocessed);
+static bool notifyReadingBeginEnvironment (tokenInfo *token, vString *envName, bool *tokenUnprocessed);
+static bool notifyReadingEndEnvironment (vString *envName);
+
+
+/*
+ *	 FUNCTION DEFINITIONS
+ */
+
+static tokenInfo *newToken (void)
+{
+	tokenInfo *const token = xMalloc (1, tokenInfo);
+
+	token->type			= TOKEN_UNDEFINED;
+	token->keyword		= KEYWORD_NONE;
+	token->string		= vStringNew ();
+	token->scope		= vStringNew ();
+	token->lineNumber   = getInputLineNumber ();
+	token->filePosition = getInputFilePosition ();
+
+	return token;
+}
+
+static void deleteToken (tokenInfo *const token)
+{
+	vStringDelete (token->string);
+	vStringDelete (token->scope);
+	eFree (token);
+}
+
+static int getScopeInfo(texKind kind, vString *const parentName)
+{
+	int parentKind = KIND_GHOST_INDEX;
+	int i;
+
+	/*
+	 * Put labels separately instead of under their scope.
+	 * Is this The Right Thing To Do?
+	 */
+	if (kind >= TEXTAG_LABEL) {
+		goto out;
+	}
+
+	/*
+	 * This abuses the enum internals somewhat, but it should be ok in this
+	 * case.
+	 */
+	/* TODO: This loop and conditions can be squashed. */
+	for (i = kind - 1; i >= TEXTAG_PART; --i) {
+		if (i == TEXTAG_SUBSECTION && vStringLength(lastSubS) > 0) {
+			parentKind = i;
+			break;
+		} else if (i == TEXTAG_SECTION && vStringLength(lastSection) > 0) {
+			parentKind = i;
+			break;
+		} else if (i == TEXTAG_CHAPTER && vStringLength(lastChapter) > 0) {
+			parentKind = i;
+			break;
+		} else if (i == TEXTAG_PART && vStringLength(lastPart) > 0) {
+			parentKind = i;
+			break;
+		}
+	}
+
+	/*
+	 * Is '""' the best way to separate scopes? It has to be something that
+	 * should ideally never occur in normal LaTeX text.
+	 */
+	for (i = TEXTAG_PART; i < (int)kind; ++i) {
+		if (i == TEXTAG_PART && vStringLength(lastPart) > 0) {
+			vStringCat(parentName, lastPart);
+		} else if (i == TEXTAG_CHAPTER && vStringLength(lastChapter) > 0) {
+			if (vStringLength(parentName) > 0) {
+				vStringCatS(parentName, "\"\"");
+			}
+			vStringCat(parentName, lastChapter);
+		} else if (i == TEXTAG_SECTION && vStringLength(lastSection) > 0) {
+			if (vStringLength(parentName) > 0) {
+				vStringCatS(parentName, "\"\"");
+			}
+			vStringCat(parentName, lastSection);
+		} else if (i == TEXTAG_SUBSECTION && vStringLength(lastSubS) > 0) {
+			if (vStringLength(parentName) > 0) {
+				vStringCatS(parentName, "\"\"");
+			}
+			vStringCat(parentName, lastSubS);
+		}
+	}
+ out:
+	return parentKind;
+}
+
+/*
+ *	 Tag generation functions
+ */
+
+struct symbolData {
+	langType lang;
+	int kind;
+	int *corkQueue;
+};
+
+static bool findTheName (int corkIndex, tagEntryInfo *entry, void *data)
+{
+	struct symbolData *symbolData = data;
+
+	if (entry->langType == symbolData->lang && entry->kindIndex == symbolData->kind)
+	{
+		/* TODO: The case operation should be removed */
+		*symbolData->corkQueue = corkIndex;
+		return false;
+	}
+	return true;
+}
+
+static int makeTexTag (tokenInfo *const token, int kind,
+					   int roleIndex, bool unique, int scopeIndex)
+{
+	int corkQueue = CORK_NIL;
+	const char *const name = vStringValue (token->string);
+
+	if (unique)
+	{
+		struct symbolData data = {
+			.lang = getInputLanguage(),
+			.kind = kind,
+			.corkQueue = &corkQueue,
+		};
+		/* TODO: The case operation should be removed */
+		if (foreachEntriesInScope (scopeIndex, name, findTheName, (void *)&data) == false)
+			return *data.corkQueue;
+	}
+
+	tagEntryInfo e;
+	initTagEntry (&e, name, kind);
+
+	e.lineNumber   = token->lineNumber;
+	e.filePosition = token->filePosition;
+
+	vString *parentName = NULL;
+
+
+	if (unique)
+		e.extensionFields.scopeIndex = scopeIndex;
+
+	/* Filling e.extensionFields.scopeKindIndex and
+	 * e.extensionFields.scopeName can be filled from "kind" parameter
+	 * of this function only when Tex parser calls this function. The
+	 * fields cannot be filled with a kind defined in a subparser.
+	 * Subparsers may fill the scope after running strategy. So in the
+	 * context of a subparser, filling the scope fields here is not
+	 * needed.
+	 */
+	if (Lang_tex == getInputLanguage ())
+	{
+		int parentKind = KIND_GHOST_INDEX;
+		parentName = vStringNew();
+		parentKind = getScopeInfo(kind, parentName);
+		if (parentKind != KIND_GHOST_INDEX) {
+			e.extensionFields.scopeKindIndex = parentKind;
+			e.extensionFields.scopeName = vStringValue(parentName);
+		}
+	}
+
+	assignRole (&e, roleIndex);
+
+	corkQueue = makeTagEntry (&e);
+	vStringDelete (parentName);	/* NULL is o.k. */
+
+	if (unique && corkQueue != CORK_NIL)
+		registerEntry (corkQueue);
+
+	return corkQueue;
+}
+
+/*
+ *	 Parsing functions
+ */
+
+/*
+ *	Read a C identifier beginning with "firstChar" and places it into
+ *	"name".
+ */
+static void parseIdentifier (vString *const string, const int firstChar)
+{
+	int c = firstChar;
+	Assert (isIdentChar (c));
+	do
+	{
+		vStringPut (string, c);
+		c = getcFromInputFile ();
+	} while (c != EOF && isIdentChar (c));
+
+	if (c != EOF)
+		ungetcToInputFile (c);		/* unget non-identifier character */
+}
+
+static bool readTokenFull (tokenInfo *const token, const bool includeWhitespaces)
+{
+	int c;
+	int whitespaces = -1;
+
+	token->type			= TOKEN_UNDEFINED;
+	token->keyword		= KEYWORD_NONE;
+	vStringClear (token->string);
+
+getNextChar:
+
+	do
+	{
+		c = getcFromInputFile ();
+		whitespaces++;
+	}
+	while (c == '\t'  ||  c == ' ' ||  c == '\n');
+
+	token->lineNumber   = getInputLineNumber ();
+	token->filePosition = getInputFilePosition ();
+
+	if (includeWhitespaces && whitespaces > 0 && c != '%' && c != EOF)
+	{
+		ungetcToInputFile (c);
+		c = ' ';
+	}
+
+	token->type = (unsigned char) c;
+	switch (c)
+	{
+		case EOF: return false;
+
+		case '\\':
+				  /*
+				   * All Tex tags start with a backslash.
+				   * Check if the next character is an alpha character
+				   * else it is not a potential tex tag.
+				   */
+				  c = getcFromInputFile ();
+				  if (! isalpha (c))
+					  ungetcToInputFile (c);
+				  else
+				  {
+					  vStringPut (token->string, '\\');
+					  parseIdentifier (token->string, c);
+					  token->keyword = lookupKeyword (vStringValue (token->string) + 1, Lang_tex);
+					  if (isKeyword (token, KEYWORD_NONE))
+						  token->type = TOKEN_IDENTIFIER;
+					  else
+						  token->type = TOKEN_KEYWORD;
+				  }
+				  break;
+
+		case '%':
+				  skipToCharacterInInputFile ('\n'); /* % are single line comments */
+				  goto getNextChar;
+				  break;
+
+		default:
+				  if (isIdentChar (c))
+				  {
+					  parseIdentifier (token->string, c);
+					  token->type = TOKEN_IDENTIFIER;
+				  }
+				  break;
+	}
+	return true;
+}
+
+static bool readToken (tokenInfo *const token)
+{
+	return readTokenFull (token, false);
+}
+
+static void copyToken (tokenInfo *const dest, tokenInfo *const src)
+{
+	dest->lineNumber = src->lineNumber;
+	dest->filePosition = src->filePosition;
+	dest->type = src->type;
+	dest->keyword = src->keyword;
+	vStringCopy (dest->string, src->string);
+	vStringCopy (dest->scope, src->scope);
+}
+
+static void updateScopeInfo (texKind kind, vString *fullname)
+{
+	switch (kind)
+	{
+		case TEXTAG_PART:
+			vStringCopy(lastPart, fullname);
+			vStringClear(lastChapter);
+			vStringClear(lastSection);
+			vStringClear(lastSubS);
+			vStringClear(lastSubSubS);
+			break;
+		case TEXTAG_CHAPTER:
+			vStringCopy(lastChapter, fullname);
+			vStringClear(lastSection);
+			vStringClear(lastSubS);
+			vStringClear(lastSubSubS);
+			break;
+		case TEXTAG_SECTION:
+			vStringCopy(lastSection, fullname);
+			vStringClear(lastSubS);
+			vStringClear(lastSubSubS);
+			break;
+		case TEXTAG_SUBSECTION:
+			vStringCopy(lastSubS, fullname);
+			vStringClear(lastSubSubS);
+			break;
+		case TEXTAG_SUBSUBSECTION:
+			vStringCopy(lastSubSubS, fullname);
+			break;
+		default:
+			break;
+	}
+}
+
+/*
+ *	 Scanning functions
+ */
+
+/* STRATEGY array represents the sequence of * expected tokens. If an
+ * input token matches the current * expectation (the current strategy),
+ * parseWithStrategy() runs * the actions attached to the strategy.
+ *
+ * The actions are making a tag with the kind specified with kindIndex
+ * field of the current strategy and/or storing a name to NAME field
+ * of the current strategy.
+ *
+ * If the input token doesn't much the current strategy, above actions
+ * are not run. If TEX_NAME_FLAG_OPTIONAL is specified in FLAGS field
+ * of the current specified, parseWithStrategy() tries the next
+ * strategy of STRATEGY array without reading a new token.  If
+ * TEX_NAME_FLAG_OPTIONAL is not in FLAGS field, parseWithStrategy()
+ * returns the control to its caller immediately.
+ *
+ * TOKENUNPROCESSED is used for both input and output.  As input,
+ * TOKENUNPROCESSED tells whether parseWithStrategy() should read a
+ * new token before matching the STRATEGY array or not.  If
+ * TOKENUNPROCESSED is true, parseWithStrategy function reads a new
+ * token before matching.  As output, TOKENUNPROCESSED tells the
+ * caller of parseWithStrategy() that a new token is already stored to
+ * TOKEN but parseWithStrategy() has not processed yet.
+ */
+static bool parseWithStrategy (tokenInfo *token,
+							   struct TexParseStrategy *strategy,
+							   bool *tokenUnprocessed)
+{
+	bool next_token = !*tokenUnprocessed;
+	tokenInfo * name = NULL;
+	bool eof = false;
+	bool exclusive = false;
+
+	for (struct TexParseStrategy *s = strategy; s->type != 0; ++s)
+		s->corkIndex = CORK_NIL;
+
+	for (struct TexParseStrategy *s = strategy; !eof && s->type != 0; ++s)
+	{
+		if (s->kindIndex != KIND_GHOST_INDEX || s->name)
+		{
+			name = newToken ();
+			break;
+		}
+	}
+
+	for (struct TexParseStrategy *s = strategy; !eof && s->type != 0; ++s)
+	{
+		bool capture_name = s->kindIndex != KIND_GHOST_INDEX || s->name;
+
+		if (next_token)
+		{
+			if (!readToken (token))
+			{
+				eof = true;
+				break;
+			}
+		}
+
+		if ((s->type == '<' && isType (token, '<'))
+			|| (s->type == '[' && isType (token, '[')))
+		{
+			tokenType terminator = (s->type == '<') ? '>' : ']';
+
+			next_token = true;
+
+
+			if (!readToken (token))
+			{
+				eof = true;
+				break;
+			}
+			if (capture_name)
+			{
+				copyToken (name, token);
+				vStringClear (name->string);
+			}
+
+			while (! isType (token, terminator))
+			{
+				if (capture_name && isType (token, TOKEN_IDENTIFIER))
+				{
+					if (vStringLength (name->string) > 0)
+						vStringPut (name->string, ' ');
+					vStringCat (name->string, token->string);
+				}
+
+				if (!readTokenFull (token,
+									s->flags & TEX_NAME_FLAG_INCLUDING_WHITESPACE))
+				{
+					eof = true;
+					break;
+				}
+			}
+			if (!exclusive && capture_name && vStringLength (name->string) > 0)
+			{
+				if (s->kindIndex != KIND_GHOST_INDEX)
+					s->corkIndex = makeTexTag (name, s->kindIndex, s->roleIndex,
+											   s->unique, s->scopeIndex);
+
+				if (s->name)
+					vStringCopy(s->name, name->string);
+
+				if (s->flags & TEX_NAME_FLAG_EXCLUSIVE)
+					exclusive = true;
+			}
+		}
+		else if (s->type == '*' && isType (token, '*'))
+			next_token = true;
+		else if (((s->type == '{' || s->type == '\\') && isType (token, '{')) ||
+			(s->type == '\\' && isType (token, TOKEN_IDENTIFIER)))
+		{
+			int depth = 1;
+			bool missing_parens = isType (token, TOKEN_IDENTIFIER);
+
+			next_token = true;
+
+			if (!missing_parens && !readToken (token))
+			{
+				eof = true;
+				break;
+			}
+			if (capture_name)
+			{
+				copyToken (name, token);
+				vStringClear (name->string);
+			}
+			if (missing_parens)
+			{
+				vStringCat (name->string, token->string);
+				depth = 0;
+			}
+
+			/* Handle the case the code like \section{} */
+			if (isType (token, '}'))
+				break;
+			while (depth > 0)
+			{
+				if (capture_name)
+				{
+					if (isType (token, TOKEN_IDENTIFIER) || isType (token, TOKEN_KEYWORD))
+						vStringCat (name->string, token->string);
+					else
+						vStringPut (name->string, token->type);
+				}
+				if (!readTokenFull (token,
+									s->flags & TEX_NAME_FLAG_INCLUDING_WHITESPACE))
+				{
+					eof = true;
+					break;
+				}
+				else if (isType (token, TOKEN_OPEN_CURLY))
+					depth++;
+				else if (isType (token, TOKEN_CLOSE_CURLY))
+					depth--;
+			}
+			if (!exclusive && depth == 0 && capture_name && vStringLength (name->string) > 0)
+			{
+				vStringStripTrailing (name->string);
+
+				if (s->kindIndex != KIND_GHOST_INDEX)
+					s->corkIndex = makeTexTag (name, s->kindIndex, s->roleIndex,
+											   s->unique, s->scopeIndex);
+
+				if (s->name)
+					vStringCopy(s->name, name->string);
+
+				if (s->flags & TEX_NAME_FLAG_EXCLUSIVE)
+					exclusive = true;
+
+			}
+		}
+		else if (s->flags & TEX_NAME_FLAG_OPTIONAL)
+			/* Apply next strategy to the same token */
+			next_token = false;
+		else
+		{
+			*tokenUnprocessed = true;
+			break;
+		}
+	}
+
+	/* The last token is optional and not present - let the caller know */
+	if (!next_token)
+		*tokenUnprocessed = true;
+
+	if (name)
+		deleteToken (name);
+
+	return eof;
+}
+
+static bool parseTagFull (tokenInfo *const token, texKind kind, int roleIndex, bool enterSquare, bool *tokenUnprocessed)
+{
+	bool eof = false;
+	vString *taggedName = vStringNew();
+
+	/*
+	 * Tex tags are of these formats:
+	 *   \keyword{any number of words}
+	 *   \keyword[short desc]{any number of words}
+	 *   \keyword*[short desc]{any number of words}
+	 *
+	 * When a keyword is found, loop through all words within
+	 * the curly braces for the tag name.
+	 *
+	 * If the keyword is label like \label, words in the square
+	 * brackets should be skipped. This can be controlled
+	 * with `enterSquare' parameter; true is for tagging, and
+	 * false is for skipping.
+	 */
+
+	struct TexParseStrategy strategy [] = {
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			/* .kindIndex is initialized dynamically. */
+		},
+		{
+			.type = '*',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '{',
+			.flags = TEX_NAME_FLAG_INCLUDING_WHITESPACE,
+			.kindIndex = kind,
+			.roleIndex = roleIndex,
+			.name = taggedName,
+			.unique = false,
+		},
+		{
+			.type = 0
+		}
+	};
+
+
+	if (enterSquare)
+	{
+		strategy [0].kindIndex = kind;
+		strategy [0].roleIndex = roleIndex;
+		strategy [0].flags |= TEX_NAME_FLAG_EXCLUSIVE;
+		strategy [0].name = taggedName;
+		strategy [0].unique = false;
+	}
+	else
+	{
+		strategy [0].kindIndex = KIND_GHOST_INDEX;
+		strategy [0].name = NULL;
+	}
+
+	if (parseWithStrategy (token, strategy, tokenUnprocessed))
+	{
+		eof = true;
+		goto out;
+	}
+
+	/*
+	 * save the name of the last section definitions for scope-resolution
+	 * later
+	 */
+	if (vStringLength (taggedName) > 0)
+		updateScopeInfo (kind, taggedName);
+
+ out:
+	vStringDelete (taggedName);
+
+	return eof;
+}
+
+static bool parseTag (tokenInfo *const token, texKind kind,
+					  bool enterSquare, bool *tokenUnprocessed)
+{
+	return parseTagFull (token, kind, ROLE_DEFINITION_INDEX,
+						 enterSquare, tokenUnprocessed);
+}
+
+static bool parseEnv (tokenInfo *const token, bool begin, bool *tokenUnprocessed)
+{
+	bool eof = false;
+	vString *envName = vStringNew ();
+	struct TexParseStrategy strategy [] = {
+		{
+			.type = '{',
+			.flags = TEX_NAME_FLAG_INCLUDING_WHITESPACE,
+			.kindIndex = begin ? TEXTAG_ENVIRONMENT : KIND_GHOST_INDEX,
+			.roleIndex = TEX_ENVIRONMENT_USED,
+			.name = envName,
+		},
+		{
+			.type = 0
+		}
+	};
+
+	if (parseWithStrategy (token, strategy, tokenUnprocessed))
+	{
+		eof = true;
+		goto out;
+	}
+
+
+	if (vStringLength (envName) > 0)
+	{
+		if (begin)
+			eof = notifyReadingBeginEnvironment (token, envName, tokenUnprocessed);
+		else
+			eof = notifyReadingEndEnvironment (envName);
+	}
+
+ out:
+	vStringDelete (envName);
+
+	return eof;
+
+}
+
+static bool parseNewcommandFull (tokenInfo *const token, bool *tokenUnprocessed, texKind kind)
+{
+	bool eof = false;
+
+	/* \newcommand{cmd}[args][opt]{def} */
+	/* \newcommand\cmd[args][opt]{def} */
+	/* \def\cmd{replacement} */
+	struct TexParseStrategy strategy [] = {
+		{
+			.type = '\\',
+			.flags = 0,
+			.kindIndex = kind,
+			.roleIndex = ROLE_DEFINITION_INDEX,
+			.name = NULL,
+			.unique = false,
+		},
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = 0
+		}
+	};
+
+	if (parseWithStrategy (token, strategy, tokenUnprocessed))
+		eof = true;
+
+	return eof;
+}
+
+static bool parseNewcommand (tokenInfo *const token, bool *tokenUnprocessed)
+{
+	return parseNewcommandFull (token, tokenUnprocessed, TEXTAG_COMMAND);
+}
+
+static bool parseNewEnvironment (tokenInfo *const token, bool *tokenUnprocessed)
+{
+	bool eof = false;
+	/* \newenvironment{nam}[args]{begdef}{enddef} */
+	struct TexParseStrategy strategy [] = {
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = TEXTAG_ENVIRONMENT,
+			.roleIndex = ROLE_DEFINITION_INDEX,
+			.name = NULL,
+			.unique = false,
+		},
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = 0
+		}
+	};
+
+	if (parseWithStrategy (token, strategy, tokenUnprocessed))
+		eof = true;
+
+	return eof;
+}
+
+static bool parseNewTheorem (tokenInfo *const token, bool *tokenUnprocessed)
+{
+	bool eof = false;
+	/*	\newtheorem{name}{title}
+		\newtheorem{name}{title}[numbered_within]
+		\newtheorem{name}[numbered_like]{title} */
+	struct TexParseStrategy strategy [] = {
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = TEXTAG_THEOREM,
+			.roleIndex = ROLE_DEFINITION_INDEX,
+			.name = NULL,
+			.unique = false,
+		},
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = 0
+		}
+	};
+
+	if (parseWithStrategy (token, strategy, tokenUnprocessed))
+		eof = true;
+
+	return eof;
+}
+
+static bool parseNewcounter (tokenInfo *const token, bool *tokenUnprocessed)
+{
+	bool eof = false;
+	/* \newcounter {counter}[parentCounter] */
+	struct TexParseStrategy strategy [] = {
+		{
+			.type = '{',
+			.flags = 0,
+			.kindIndex = TEXTAG_COUNTER,
+			.roleIndex = ROLE_DEFINITION_INDEX,
+			.name = NULL,
+			.unique = false,
+		},
+		{
+			.type = '[',
+			.flags = TEX_NAME_FLAG_OPTIONAL,
+			.kindIndex = KIND_GHOST_INDEX,
+			.name = NULL,
+		},
+		{
+			.type = 0
+		}
+	};
+
+	if (parseWithStrategy (token, strategy, tokenUnprocessed))
+		eof = true;
+
+	return eof;
+}
+
+static void parseTexFile (tokenInfo *const token)
+{
+	bool eof = false;
+	bool tokenUnprocessed = false;
+
+	do
+	{
+		if (!tokenUnprocessed)
+		{
+			if (!readToken (token))
+				break;
+		}
+		tokenUnprocessed = false;
+
+		if (isType (token, TOKEN_KEYWORD))
+		{
+			switch (token->keyword)
+			{
+				case KEYWORD_part:
+					eof = parseTag (token, TEXTAG_PART, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_chapter:
+					eof = parseTag (token, TEXTAG_CHAPTER, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_section:
+					eof = parseTag (token, TEXTAG_SECTION, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_subsection:
+					eof = parseTag (token, TEXTAG_SUBSECTION, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_subsubsection:
+					eof = parseTag (token, TEXTAG_SUBSUBSECTION, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_paragraph:
+					eof = parseTag (token, TEXTAG_PARAGRAPH, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_subparagraph:
+					eof = parseTag (token, TEXTAG_SUBPARAGRAPH, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_label:
+					eof = parseTag (token, TEXTAG_LABEL, false, &tokenUnprocessed);
+					break;
+				case KEYWORD_include:
+					eof = parseTagFull (token, TEXTAG_XINPUT, TEX_XINPUT_INCLUDED,
+										false, &tokenUnprocessed);
+					break;
+				case KEYWORD_input:
+					eof = parseTagFull (token, TEXTAG_XINPUT, TEX_XINPUT_INPUT,
+										false, &tokenUnprocessed);
+					break;
+				case KEYWORD_begin:
+					eof = parseEnv (token, true, &tokenUnprocessed);
+					break;
+				case KEYWORD_end:
+					eof = parseEnv (token, false, &tokenUnprocessed);
+					break;
+				case KEYWORD_bibitem:
+					eof = parseTag (token, TEXTAG_BIBITEM, false, &tokenUnprocessed);
+					break;
+				case KEYWORD_bibliography:
+					eof = parseTagFull (token, TEXTAG_XINPUT, TEX_XINPUT_BIBLIOGRAPHY,
+										false, &tokenUnprocessed);
+					break;
+				case KEYWORD_newcommand:
+				case KEYWORD_renewcommand:
+				case KEYWORD_providecommand:
+				case KEYWORD_def:
+					eof = parseNewcommand (token, &tokenUnprocessed);
+					break;
+				case KEYWORD_declaremathoperator:
+					eof = parseNewcommandFull (token, &tokenUnprocessed, TEXTAG_OPERATOR);
+					break;
+				case KEYWORD_newenvironment:
+				case KEYWORD_renewenvironment:
+					eof = parseNewEnvironment (token, &tokenUnprocessed);
+					break;
+				case KEYWORD_newtheorem:
+					eof = parseNewTheorem (token, &tokenUnprocessed);
+					break;
+				case KEYWORD_newcounter:
+					eof = parseNewcounter (token, &tokenUnprocessed);
+					break;
+				default:
+					break;
+			}
+		}
+		else if (isType (token, TOKEN_IDENTIFIER))
+			eof = notifyReadingIdentifier (token, &tokenUnprocessed);
+		if (eof)
+			break;
+	} while (true);
+}
+
+static void initialize (const langType language)
+{
+	Assert (ARRAY_SIZE (TexKinds) == TEXTAG_COUNT);
+	Lang_tex = language;
+
+	lastPart    = vStringNew();
+	lastChapter = vStringNew();
+	lastSection = vStringNew();
+	lastSubS    = vStringNew();
+	lastSubSubS = vStringNew();
+}
+
+static void finalize (const langType language CTAGS_ATTR_UNUSED,
+		      bool initialized)
+{
+	if (initialized)
+	{
+		vStringDelete(lastPart);
+		lastPart = NULL;
+		vStringDelete(lastChapter);
+		lastChapter = NULL;
+		vStringDelete(lastSection);
+		lastSection = NULL;
+		vStringDelete(lastSubS);
+		lastSubS = NULL;
+		vStringDelete(lastSubSubS);
+		lastSubSubS = NULL;
+	}
+}
+
+static void findTexTags (void)
+{
+	tokenInfo *const token = newToken ();
+
+	vStringClear(lastPart);
+	vStringClear(lastChapter);
+	vStringClear(lastSection);
+	vStringClear(lastSubS);
+	vStringClear(lastSubSubS);
+
+	parseTexFile (token);
+
+	deleteToken (token);
+}
+
+static bool notifyReadingIdentifier (tokenInfo *id_token, bool *tokenUnprocessed)
+{
+	subparser *sub;
+	bool eof = false;
+
+	foreachSubparser (sub, false)
+	{
+		texSubparser *texsub = (texSubparser *)sub;
+
+		if (texsub->readIdentifierNotify)
+		{
+			struct TexParseStrategy *strategy;
+
+			enterSubparser(sub);
+
+			strategy = texsub->readIdentifierNotify (texsub, id_token->string);
+
+			if (strategy)
+			{
+				eof = parseWithStrategy (id_token, strategy, tokenUnprocessed);
+				if (texsub->reportStrategicParsing)
+					texsub->reportStrategicParsing (texsub, strategy);
+			}
+
+			leaveSubparser();
+
+			if (strategy)
+				break;
+		}
+	}
+
+	return eof;
+}
+
+static bool notifyReadingBeginEnvironment (tokenInfo *token,
+										   vString *envName,
+										   bool *tokenUnprocessed)
+{
+	subparser *sub;
+	bool eof = false;
+
+	foreachSubparser (sub, false)
+	{
+		texSubparser *texsub = (texSubparser *)sub;
+
+		if (texsub->readEnviromentBeginNotify)
+		{
+			struct TexParseStrategy *strategy;
+
+			enterSubparser (sub);
+			strategy = texsub->readEnviromentBeginNotify (texsub, envName);
+			if (strategy)
+			{
+				eof = parseWithStrategy (token, strategy, tokenUnprocessed);
+				if (texsub->reportStrategicParsing)
+					texsub->reportStrategicParsing (texsub, strategy);
+			}
+			leaveSubparser ();
+			if (strategy)
+				break;
+		}
+	}
+
+	return eof;
+}
+
+static bool notifyReadingEndEnvironment (vString  *envName)
+{
+	subparser *sub;
+
+	foreachSubparser (sub, false)
+	{
+		texSubparser *texsub = (texSubparser *)sub;
+
+		if (texsub->readEnviromentEndNotify)
+		{
+			bool consuming;
+
+			enterSubparser (sub);
+			consuming = texsub->readEnviromentEndNotify (texsub, envName);
+			leaveSubparser ();
+			if (consuming)
+				break;
+		}
+	}
+
+	return false;
+}
+
+/* Create parser definition structure */
+extern parserDefinition* TexParser (void)
+{
+	static const char *const extensions [] = { "tex", NULL };
+	parserDefinition *const def = parserNew ("Tex");
+	def->extensions = extensions;
+	/*
+	 * New definitions for parsing instead of regex
+	 */
+	def->kindTable	= TexKinds;
+	def->kindCount	= ARRAY_SIZE (TexKinds);
+	def->parser		= findTexTags;
+	def->initialize = initialize;
+	def->finalize   = finalize;
+	def->keywordTable =  TexKeywordTable;
+	def->keywordCount = ARRAY_SIZE (TexKeywordTable);
+	def->useCork = CORK_QUEUE  | CORK_SYMTAB;
+	return def;
+}


Modified: ctags/parsers/tex.h
114 lines changed, 114 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,114 @@
+/*
+*   Copyright (c) 2020, Masatake YAMATO
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License version 2 or (at your option) any later version.
+*
+*   Tex base parser interface exported to subparsers
+*/
+
+#ifndef CTAGS_PARSER_TEX_H
+#define CTAGS_PARSER_TEX_H
+
+/*
+*	 INCLUDE FILES
+*/
+
+#include "general.h"  /* must always come first */
+
+#include "subparser.h"
+#include "vstring.h"
+
+
+/*
+*	 DATA DEFINITIONS
+*/
+
+/* Parsing strategy */
+
+enum TexNameFlag {
+	/* Allow that the type of input token doesn't match
+	 * the type of strategy. In stread of aborting,
+	 * apply the next strategy to the same token. */
+	TEX_NAME_FLAG_OPTIONAL             = (1 << 0),
+
+	/* When reading tokens inside pair,
+	 * whitespaces are considered as parts of a token or not. */
+	TEX_NAME_FLAG_INCLUDING_WHITESPACE = (1 << 1),
+
+	/* If a tag is created with this strategy, don't
+	 * create a tag in its successor strategies. */
+	TEX_NAME_FLAG_EXCLUSIVE            = (1 << 2),
+};
+
+struct TexParseStrategy {
+	/* Expected token type '<', '[', '*', '{', and '\\' are supported.
+	 * 0 means the end of strategies. '\\' means {} pair may be omitted.
+	 *
+	 * A string between <>, [], or {} (pairs) can be tagged or store to
+	 * a vString. See kindIndex and name field of this structure.
+	 */
+	int type;
+
+	/* Bits combination of enum TexNameFlag */
+	unsigned int flags;
+
+	/* Kind and role for making a tag for the string surrounded by one of pairs.
+	 * If you don't need to make a tag for the string,
+	 * specify KIND_GHOST_INDEX. */
+	int kindIndex;
+	int roleIndex;
+
+	/* If a tag is made, Tex parser stores its cork index here. */
+	int corkIndex;
+
+	/* Store the string surrounded by one of paris.
+	 * If you don't need to store the string, set NULL here. */
+	vString *name;
+
+	/* If true, make at most one tag for the name in the scope specified
+	 * with scopeIndex. When making a tag, scopeIndex is set to
+	 * extensionFields.scopeIndex only if unique is true.
+	 * scopeIndex is never referred if unique if false. */
+	bool unique;
+	int scopeIndex;
+};
+
+typedef struct sTexSubparser texSubparser;
+struct sTexSubparser {
+	subparser subparser;
+
+	/* When Tex parser reads an \begin{foo}, it calls
+	 * this method.
+	 *
+	 * A subparser having interests in successor tokens may return strategies.
+	 * If it doesn't, just return NULL; Tex base parser may call the next subparser.
+	 */
+	struct TexParseStrategy * (* readEnviromentBeginNotify) (texSubparser *s,
+															 vString *env);
+	/* When Tex parser reads an \end{foo}, it calls
+	 * this method.
+	 *
+	 * If this method returns true, Tex base parser may call the next subparser.
+	 * If it returns false, Tex base parser stops calling the rest of subparsers.
+	 */
+	bool (* readEnviromentEndNotify) (texSubparser *s, vString *env);
+
+	/* When Tex parser reads an \identifier, it calls
+	 * this method.
+	 *
+	 * A subparser having interests in successor tokens may return strategies.
+	 * If it has no interest, just return NULL; Tex base parser may call next subparser.
+	 */
+	struct TexParseStrategy *(* readIdentifierNotify) (texSubparser *s,
+													   vString *identifier);
+
+	/* After Tex parser runs the strategies returned from readIdentifierNotify
+	 * method, Tex parser calls this method to notify the subparser the result
+	 * of running the strategies; corkIndex and/or name fields of strategies
+	 * may be filled. */
+	void (* reportStrategicParsing) (texSubparser *s,
+									 const struct TexParseStrategy *strategy);
+};
+
+#endif	/* CTAGS_PARSER_TEX_H */


Modified: meson.build
3 lines changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -637,7 +637,6 @@ ctags = static_library('ctags',
 	'ctags/parsers/geany_lcpp.c',
 	'ctags/parsers/geany_lcpp.h',
 	'ctags/parsers/geany_matlab.c',
-	'ctags/parsers/geany_tex.c',
 	'ctags/parsers/go.c',
 	'ctags/parsers/haskell.c',
 	'ctags/parsers/haxe.c',
@@ -670,6 +669,8 @@ ctags = static_library('ctags',
 	'ctags/parsers/tcl.c',
 	'ctags/parsers/tcl.h',
 	'ctags/parsers/tcloo.c',
+	'ctags/parsers/tex.c',
+	'ctags/parsers/tex.h',
 	'ctags/parsers/txt2tags.c',
 	'ctags/parsers/verilog.c',
 	'ctags/parsers/vhdl.c',


Modified: src/tagmanager/tm_parser.c
29 lines changed, 21 insertions(+), 8 deletions(-)
===================================================================
@@ -204,22 +204,32 @@ static TMParserMapGroup group_PYTHON[] = {
 };
 
 static TMParserMapEntry map_LATEX[] = {
-	{'f', tm_tag_function_t},
-	{'c', tm_tag_class_t},
-	{'m', tm_tag_member_t},
-	{'d', tm_tag_macro_t},
-	{'v', tm_tag_variable_t},
-	{'n', tm_tag_namespace_t},
-	{'s', tm_tag_struct_t},
+	{'p', tm_tag_enum_t},       // part
+	{'c', tm_tag_namespace_t},  // chapter
+	{'s', tm_tag_member_t},     // section
+	{'u', tm_tag_macro_t},      // subsection
+	{'b', tm_tag_variable_t},   // subsubsection
+	{'P', tm_tag_undef_t},      // paragraph
+	{'G', tm_tag_undef_t},      // subparagraph
+	{'l', tm_tag_struct_t},     // label
+	{'i', tm_tag_undef_t},      // xinput
+	{'B', tm_tag_field_t},      // bibitem
+	{'C', tm_tag_function_t},   // command
+	{'o', tm_tag_function_t},   // operator
+	{'e', tm_tag_class_t},      // environment
+	{'t', tm_tag_class_t},      // theorem
+	{'N', tm_tag_undef_t},      // counter
 };
 static TMParserMapGroup group_LATEX[] = {
 	{_("Command"), TM_ICON_NONE, tm_tag_function_t},
 	{_("Environment"), TM_ICON_NONE, tm_tag_class_t},
+	{_("Part"), TM_ICON_NONE, tm_tag_enum_t},
+	{_("Chapter"), TM_ICON_NONE, tm_tag_namespace_t},
 	{_("Section"), TM_ICON_NONE, tm_tag_member_t},
 	{_("Subsection"), TM_ICON_NONE, tm_tag_macro_t},
 	{_("Subsubsection"), TM_ICON_NONE, tm_tag_variable_t},
+	{_("Bibitem"), TM_ICON_NONE, tm_tag_field_t},
 	{_("Label"), TM_ICON_NONE, tm_tag_struct_t},
-	{_("Chapter"), TM_ICON_NONE, tm_tag_namespace_t},
 };
 
 // no scope information
@@ -1472,6 +1482,7 @@ const gchar *tm_parser_scope_separator(TMParserType lang)
 		case TM_PARSER_ZEPHIR:
 			return "::";
 
+		case TM_PARSER_LATEX:
 		case TM_PARSER_MARKDOWN:
 		case TM_PARSER_TXT2TAGS:
 			return "\"\"";
@@ -1495,6 +1506,7 @@ const gchar *tm_parser_scope_separator_printable(TMParserType lang)
 	{
 		case TM_PARSER_ASCIIDOC:
 		case TM_PARSER_CONF:
+		case TM_PARSER_LATEX:
 		case TM_PARSER_MARKDOWN:
 		case TM_PARSER_REST:
 		case TM_PARSER_TXT2TAGS:
@@ -1523,6 +1535,7 @@ gboolean tm_parser_has_full_scope(TMParserType lang)
 		case TM_PARSER_JAVA:
 		case TM_PARSER_JAVASCRIPT:
 		case TM_PARSER_JSON:
+		case TM_PARSER_LATEX:
 		case TM_PARSER_LUA:
 		case TM_PARSER_MARKDOWN:
 		case TM_PARSER_PHP:


Modified: tests/ctags/3526726.tex.tags
266 lines changed, 130 insertions(+), 136 deletions(-)
===================================================================
@@ -1,155 +1,149 @@
 # format=tagmanager
- I think I found a bug in Snort. Now what?�65536�0
- I've got RedHat and ....�65536�0
-A Rule with PCRE causes a failure to load snort.conf.  Why?�65536�0
-After I add new rules or comment out rules how do I make Snort reload?�65536�0
-Are rule keywords ORed or ANDed together?�65536�0
-Are there other output systems for Snort besides ``Barnyard''?\label{spoolers�65536�0
-BASE appears to be broken in Lynx �65536�0
+A Rule with PCRE causes a failure to load snort.conf. Why?�65536�Problems�0
+After I add new rules or comment out rules how do I make Snort reload?�65536�Rules and Alerts�0
+Are rule keywords ORed or ANDed together?�65536�Rules and Alerts�0
+Are there other output systems for Snort besides ``Barnyard''?\label{spoolers}�65536�Getting Fancy�0
+BASE appears to be broken in Lynx�65536�Problems�0
 Background�64�0
-Can Snort be evaded by the use of polymorphic mutators on shellcode?�65536�0
-Can Snort trigger a rule by MAC addresses?�65536�0
-Can priorities be assigned to alerts using BASE?  �65536�0
+Can Snort be evaded by the use of polymorphic mutators on shellcode?�65536�Background�0
+Can Snort trigger a rule by MAC addresses?�65536�Rules and Alerts�0
+Can priorities be assigned to alerts using BASE?�65536�Rules and Alerts�0
 Configuring Snort�64�0
 Development�64�0
-Does Snort handle IP defragmentation?�65536�0
-Does Snort log the full packets when it generates alerts? �65536�0
-Does Snort perform TCP stream reassembly?�65536�0
-Does Snort perform stateful protocol analysis?�65536�0
-Does snort see packets filtered by IPTables/IPChains/IPF/PF?�65536�0
-Errors loading rules files�65536�0
+Does Snort handle IP defragmentation?�65536�Background�0
+Does Snort log the full packets when it generates alerts?�65536�Background�0
+Does Snort perform TCP stream reassembly?�65536�Background�0
+Does Snort perform stateful protocol analysis?�65536�Background�0
+Does snort see packets filtered by IPTables/IPChains/IPF/PF?�65536�Rules and Alerts�0
+Errors loading rules files�65536�Rules and Alerts�0
 Getting Fancy�64�0
 Getting Started�64�0
-How can I deactivate a rule?�65536�0
-How can I define an address to be anything except some hosts?�65536�0
-How can I examine logged packets in more detail?�65536�0
-How can I protect web servers running on ports other than 80?�65536�0
-How can I run Snort on multiple interfaces simultaneously?�65536�0
-How can I specify a list of ports in a rule?�65536�0
-How can I test Snort without having an Ethernet card or a connection to other computers?  �65536�0
-How can I use Snort to log HTTP URLs or SMTP traffic?�65536�0
-How do I build this BASE thing?�65536�0
-How do I configure stream4?�65536�0
-How do I get Snort and ACID working?�65536�0
-How do I get Snort to e-mail me alerts?�65536�0
-How do I get Snort to log the packet payload as well as the header?�65536�0
-How do I ignore traffic coming from a particular host or hosts?�65536�0
-How do I log a specific type of traffic and send alerts to syslog?�65536�0
-How do I log to multiple databases or output plugins?�65536�0
-How do I process those Snort logs into reports?�65536�0
-How do I run Snort?�65536�0
-How do I set EXTERNAL\_NET?�65536�0
-How do I setup a receive-only ethernet cable?�65536�0
-How do I setup snort on a `stealth' interface? �65536�0
-How do I test Snort alerts and logging?�65536�0
-How do I turn off ``spp:possible EVASIVE RST detection'' alerts?�65536�0
-How do I understand this traffic and do IDS alert analysis?�65536�0
-How do I use a remote syslog machine?�65536�0
-How do you get Snort to ignore some traffic?�65536�0
-How do you pronounce the names of some of these guys who work on Snort?�65536�0
-How do you put Snort in debug mode? �65536�0
-How does rule ordering work?�65536�0
-How long can address lists, variables, or rules be?�65536�0
-How to start Snort as a win32 service? �65536�0
-I am getting `snort [pid] uses obsolete (PF\_INET, SOCK\_PACKET)' warnings. What's wrong?�65536�0
-I am getting too many ``IIS Unicode attack detected'' and/or ``CGI Null Byte attack detected'' false positives.  How can I turn this detection off? �65536�0
-I am still getting bombarded with spp\_portscan messages even though the IP that I am getting the portscan from is in my \$DNS\_SERVERs var �65536�0
-I am using Snort on Windows and receive an ``OpenPcap() error upon startup: ERROR: OpenPcap() device open: Error opening adapter'' message. What's wrong? �65536�0
-I have one network card and two aliases, how can I force Snort to ``listen'' on both addresses?�65536�0
-I hear people talking about ``Barnyard''. What's that?\label{barnyard�65536�0
-I just downloaded a new ruleset and now Snort fails, complaining about the�65536�0
-I try to start Snort and it gives an error like ``ERROR: Unable to open�65536�0
-I want to build a Snort box.  Will this $<$Insert list of hardware$>$ handle $<$this much$>$ traffic? �65536�0
-I'm getting large amounts of $<$some alerts type$>$. What should I do?  Where can I go to find out more about it? �65536�0
-I'm getting lots of *ICMP Ping Speedera*, is this bad?�65536�0
-I'm not seeing any interfaces listed under Win32.�65536�0
-I'm on a switched network, can I still use Snort?�65536�0
+How can I deactivate a rule?�65536�Rules and Alerts�0
+How can I define an address to be anything except some hosts?�65536�Rules and Alerts�0
+How can I examine logged packets in more detail?�65536�Getting Fancy�0
+How can I protect web servers running on ports other than 80?�65536�Rules and Alerts�0
+How can I run Snort on multiple interfaces simultaneously?�65536�Configuring Snort�0
+How can I specify a list of ports in a rule?�65536�Rules and Alerts�0
+How can I test Snort without having an Ethernet card or a connection to other computers?�65536�Getting Fancy�0
+How can I use Snort to log HTTP URLs or SMTP traffic?�65536�Getting Fancy�0
+How do I build this BASE thing?�65536�Configuring Snort�0
+How do I configure stream4?�65536�Configuring Snort�0
+How do I get Snort and ACID working?�65536�Configuring Snort�0
+How do I get Snort to e-mail me alerts?�65536�Getting Fancy�0
+How do I get Snort to log the packet payload as well as the header?�65536�Configuring Snort�0
+How do I ignore traffic coming from a particular host or hosts?�65536�Configuring Snort�0
+How do I log a specific type of traffic and send alerts to syslog?�65536�Getting Fancy�0
+How do I log to multiple databases or output plugins?�65536�Getting Fancy�0
+How do I process those Snort logs into reports?�65536�Getting Fancy�0
+How do I run Snort?�65536�Getting Started�0
+How do I set EXTERNAL\_NET?�65536�Configuring Snort�0
+How do I setup a receive-only ethernet cable?�65536�Configuring Snort�0
+How do I setup snort on a `stealth' interface?�65536�Configuring Snort�0
+How do I test Snort alerts and logging?�65536�Rules and Alerts�0
+How do I turn off ``spp:possible EVASIVE RST detection'' alerts?�65536�Rules and Alerts�0
+How do I understand this traffic and do IDS alert analysis?�65536�Getting Fancy�0
+How do I use a remote syslog machine?�65536�Configuring Snort�0
+How do you get Snort to ignore some traffic?�65536�Configuring Snort�0
+How do you pronounce the names of some of these guys who work on Snort?�65536�Background�0
+How do you put Snort in debug mode?�65536�Development�0
+How does rule ordering work?�65536�Configuring Snort�0
+How long can address lists, variables, or rules be?�65536�Rules and Alerts�0
+How to start Snort as a win32 service?�65536�Getting Fancy�0
+I am getting `snort [pid] uses obsolete (PF\_INET, SOCK\_PACKET)' warnings. What's wrong?�65536�Problems�0
+I am getting too many ``IIS Unicode attack detected'' and/or ``CGI Null Byte attack detected'' false positives. How can I turn this detection off?�65536�Rules and Alerts�0
+I am still getting bombarded with spp\_portscan messages even though the IP that I am getting the portscan from is in my \$DNS\_SERVERs var�65536�Problems�0
+I am using Snort on Windows and receive an ``OpenPcap() error upon startup: ERROR: OpenPcap() device open: Error opening adapter'' message. What's wrong?�65536�Problems�0
+I have one network card and two aliases, how can I force Snort to ``listen'' on both addresses?�65536�Configuring Snort�0
+I hear people talking about ``Barnyard''. What's that?\label{barnyard}�65536�Getting Fancy�0
+I just downloaded a new ruleset and now Snort fails, complaining about the rules.�65536�Problems�0
+I think I found a bug in Snort. Now what?�65536�Problems�0
+I try to start Snort and it gives an error like ``ERROR: Unable to open rules file: /root/.snortrc or /root//root/.snortrc.'' What can I do to fix this?�65536�Problems�0
+I want to build a Snort box. Will this $<$Insert list of hardware$>$ handle $<$this much$>$ traffic?�65536�Getting Started�0
+I'm getting large amounts of $<$some alerts type$>$. What should I do? Where can I go to find out more about it?�65536�Rules and Alerts�0
+I'm getting lots of *ICMP Ping Speedera*, is this bad?�65536�Problems�0
+I'm not seeing any interfaces listed under Win32.�65536�Problems�0
+I'm on a switched network, can I still use Snort?�65536�Background�0
+I've got RedHat and ....�65536�Getting Started�0
 IDSCenter�2048�0
-Is Fyodor Yarochkin the same Fyodor who wrote nmap?�65536�0
-Is Snort vulnerable to IDS noise generators like ``Stick'' and ``Snot''?�65536�0
-Is it possible to have Snort call an external program when an alert is raised?�65536�0
-Is it possible with snort to add a ipfilter/ipfw rule to a firewall? �65536�0
-Is there a private SID number range so my rules don't conflict?�65536�0
-It's not working on Win32, how can I tell if my problem is Snort or�65536�0
-Libpcap complains about permissions problems, what's going on?�65536�0
+Is Fyodor Yarochkin the same Fyodor who wrote nmap?�65536�Background�0
+Is Snort vulnerable to IDS noise generators like ``Stick'' and ``Snot''?�65536�Background�0
+Is it possible to have Snort call an external program when an alert is raised?�65536�Getting Fancy�0
+Is it possible with snort to add a ipfilter/ipfw rule to a firewall?�65536�Getting Fancy�0
+Is there a private SID number range so my rules don't conflict?�65536�Rules and Alerts�0
+It's not working on Win32, how can I tell if my problem is Snort or WinPcap?�65536�Problems�0
+Libpcap complains about permissions problems, what's going on?�65536�Getting Started�0
 Miscellaneous�64�0
-My /var/log/snort directory gets very large...�65536�0
-My BASE db connection times-out when performing long operations (e.g.�65536�0
-My IP address is assigned dynamically to my interface, can I use Snort with it?�65536�0
-My network spans multiple subnets.  How do I define HOME\_NET?�65536�0
-My snort crashes, how do I restart it?�65536�0
-On HPUX I get device lan0 open: recv\_ack: promisc\_phys: Invalid argument�65536�0
-Portscans are not being logged to my database �65536�0
+My /var/log/snort directory gets very large...�65536�Problems�0
+My BASE db connection times-out when performing long operations (e.g. deleting a large number of alerts).�65536�Problems�0
+My IP address is assigned dynamically to my interface, can I use Snort with it?�65536�Configuring Snort�0
+My network spans multiple subnets. How do I define HOME\_NET?�65536�Configuring Snort�0
+My snort crashes, how do I restart it?�65536�Problems�0
+On HPUX I get device lan0 open: recv\_ack: promisc\_phys: Invalid argument�65536�Problems�0
+Portscans are not being logged to my database�65536�Problems�0
 Problems�64�0
 Rules and Alerts�64�0
-SMB alerts aren't working, what's wrong? �65536�0
-Snort complains about the ``react'' keyword...�65536�0
-Snort fails to respond to a kill signal on Linux.  Why?�65536�0
-Snort is behind a firewall (ipf/pf/ipchains/ipfilter) and awfully quiet...�65536�0
-Snort is dying with a `can not create file' error and I have plenty of diskspace. What's wrong?�65536�0
-Snort is not logging to my database�65536�0
-Snort is not logging to syslog�65536�0
-Snort says BACKDOOR SIGNATURE... does my machine have a Trojan? �65536�0
-Snort says ``Garbage Packet with Null Pointer discarded!'' Huh?�65536�0
-Snort says ``Ran Out Of Space.'' Huh?�65536�0
-Snort says ``Rule IP addr (``1.1.1.1'') didn't x-late, WTF?''�65536�0
-Trying to install snort it says: ``bad interpreter: No such file or�65536�0
-What about `SMB Name Wildcard' alerts? �65536�0
-What about ``CGI Null Byte attacks?'' �65536�0
-What about all these false alarms? �65536�0
-What are CIDR netmasks? �65536�0
-What are HOME\_NET and EXTERNAL\_NET?�65536�0
-What are all these ICMP files in subdirectories under /var/log/snort? �65536�0
-What are all these ``ICMP destination unreachable'' alerts? �65536�0
-What are some resources that I can use to understand more about source�65536�0
-What are these IDS codes in the alert names? �65536�0
-What do the numbers (ie: [116:56:1]) in front of a Snort alert mean?�65536�0
-What is the best way to use Snort to block attack traffic?�65536�0
-What is the difference between ``Alerting'' and ``Logging''?�65536�0
-What is the use of the ``-r'' switch to read tcpdump files?  �65536�0
-What the heck is a SYNFIN scan?�65536�0
-What the heck is a SYNFIN scan? �65536�0
-What the heck is a ``Stealth scan''?�65536�0
-What version of Winpcap do I need?\label{winpcap�65536�0
-What's this about a Snort drinking game?�65536�0
-Where are my log files located?  What are they named?�65536�0
-Where can I get more reading and courses about IDS?\label{courses�65536�0
-Where do I find binary packages for BlueHat BSD-Linux-RT?�65536�0
-Where do I get more help on Snort?�65536�0
-Where do I get the latest version of Winpcap?�65536�0
-Where do I get the latest version of libpcap? �65536�0
-Where do the distance and within keywords work from to modify content�65536�0
-Where does one obtain new/modifed rules? How do you merge them in?�65536�0
-Where's a good place to physically put a Snort sensor?�65536�0
-Which takes precedence, commandline or rule file ?�65536�0
-Why am I seeing so many ``SMTP RCPT TO overflow'' alerts ?�65536�0
-Why are my unified alert times off by +/- N hours?�65536�0
-Why are there no subdirectories under /var/log/snort for IP addresses?�65536�0
-Why can't snort see one of the 10Mbps or 100Mbps traffic on my autoswitch hub?�65536�0
-Why do certain alerts seem to have `unknown' IPs in BASE?  �65536�0
-Why do many Snort rules have the flags P (TCP PuSH) and A (TCP ACK) set? �65536�0
-Why does Snort complain about /var/log/snort?�65536�0
-Why does building Snort complain about missing references? �65536�0
-Why does building snort fail with errors about yylex and lex\_init? �65536�0
-Why does chrooted Snort die when I send it a SIGHUP? \label{chroot�65536�0
-Why does snort report ``Packet loss statistics are unavailable under Linux?''�65536�0
-Why does the `error deleting alert' message occur when attempting to delete an alert with BASE?  �65536�0
-Why does the portscan plugin log ``stealth'' packets even though the host is in the portscan-ignorehosts list? �65536�0
-Why does the program generate alerts on packets that have pass rules?  �65536�0
-barnyard�2048�0
+SMB alerts aren't working, what's wrong?�65536�Problems�0
+Snort complains about the ``react'' keyword...�65536�Getting Fancy�0
+Snort fails to respond to a kill signal on Linux. Why?�65536�Problems�0
+Snort is behind a firewall (ipf/pf/ipchains/ipfilter) and awfully quiet...�65536�Rules and Alerts�0
+Snort is dying with a `can not create file' error and I have plenty of diskspace. What's wrong?�65536�Problems�0
+Snort is not logging to my database�65536�Problems�0
+Snort is not logging to syslog�65536�Problems�0
+Snort says BACKDOOR SIGNATURE... does my machine have a Trojan?�65536�Rules and Alerts�0
+Snort says ``Garbage Packet with Null Pointer discarded!'' Huh?�65536�Problems�0
+Snort says ``Ran Out Of Space.'' Huh?�65536�Problems�0
+Snort says ``Rule IP addr (``1.1.1.1'') didn't x-late, WTF?''�65536�Rules and Alerts�0
+Trying to install snort it says: ``bad interpreter: No such file or directory''�65536�Problems�0
+What about `SMB Name Wildcard' alerts?�65536�Rules and Alerts�0
+What about ``CGI Null Byte attacks?''�65536�Rules and Alerts�0
+What about all these false alarms?�65536�Rules and Alerts�0
+What are CIDR netmasks?�65536�Getting Started�0
+What are HOME\_NET and EXTERNAL\_NET?�65536�Configuring Snort�0
+What are all these ICMP files in subdirectories under /var/log/snort?�65536�Rules and Alerts�0
+What are all these ``ICMP destination unreachable'' alerts?�65536�Rules and Alerts�0
+What are some resources that I can use to understand more about source addresses logged and where they are coming from?�65536�Getting Fancy�0
+What are these IDS codes in the alert names?�65536�Rules and Alerts�0
+What do the numbers (ie: [116:56:1]) in front of a Snort alert mean?�65536�Rules and Alerts�0
+What is the best way to use Snort to block attack traffic?�65536�Getting Fancy�0
+What is the difference between ``Alerting'' and ``Logging''?�65536�Rules and Alerts�0
+What is the use of the ``-r'' switch to read tcpdump files?�65536�Getting Started�0
+What the heck is a SYNFIN scan?�65536�Configuring Snort�0
+What the heck is a SYNFIN scan?�65536�Rules and Alerts�0
+What the heck is a ``Stealth scan''?�65536�Configuring Snort�0
+What version of Winpcap do I need?\label{winpcap}�65536�Getting Started�0
+What's this about a Snort drinking game?�65536�Miscellaneous�0
+Where are my log files located? What are they named?�65536�Getting Started�0
+Where can I get more reading and courses about IDS?\label{courses}�65536�Background�0
+Where do I find binary packages for BlueHat BSD-Linux-RT?�65536�Getting Started�0
+Where do I get more help on Snort?�65536�Background�0
+Where do I get the latest version of Winpcap?�65536�Getting Started�0
+Where do I get the latest version of libpcap?�65536�Getting Started�0
+Where do the distance and within keywords work from to modify content searches in rules?�65536�Rules and Alerts�0
+Where does one obtain new/modifed rules? How do you merge them in?�65536�Configuring Snort�0
+Where's a good place to physically put a Snort sensor?�65536�Getting Started�0
+Which takes precedence, commandline or rule file ?�65536�Configuring Snort�0
+Why am I seeing so many ``SMTP RCPT TO overflow'' alerts ?�65536�Problems�0
+Why are my unified alert times off by +/- N hours?�65536�Problems�0
+Why are there no subdirectories under /var/log/snort for IP addresses?�65536�Configuring Snort�0
+Why can't snort see one of the 10Mbps or 100Mbps traffic on my autoswitch hub?�65536�Problems�0
+Why do certain alerts seem to have `unknown' IPs in BASE?�65536�Rules and Alerts�0
+Why do many Snort rules have the flags P (TCP PuSH) and A (TCP ACK) set?�65536�Rules and Alerts�0
+Why does Snort complain about /var/log/snort?�65536�Getting Started�0
+Why does building Snort complain about missing references?�65536�Getting Started�0
+Why does building snort fail with errors about yylex and lex\_init?�65536�Getting Started�0
+Why does chrooted Snort die when I send it a SIGHUP? \label{chroot}�65536�Problems�0
+Why does snort report ``Packet loss statistics are unavailable under Linux?''�65536�Problems�0
+Why does the `error deleting alert' message occur when attempting to delete an alert with BASE?�65536�Problems�0
+Why does the portscan plugin log ``stealth'' packets even though the host is in the portscan-ignorehosts list?�65536�Configuring Snort�0
+Why does the program generate alerts on packets that have pass rules?�65536�Rules and Alerts�0
+\myquote�16�0
+\myref�16�0
 center�1�0
-chroot�2048�0
-courses�2048�0
 document�1�0
 enumerate�1�0
 itemize�1�0
 latexonly�1�0
-myquote�16�0
-myref�16�0
-quote�1�0
-spoolers�2048�0
 stealth�2048�0
 stream4�2048�0
 tabular�1�0
 verbatim�1�0
-winpcap�2048�0


Modified: tests/ctags/bug2886870.tex.tags
19 lines changed, 10 insertions(+), 9 deletions(-)
===================================================================
@@ -1,16 +1,19 @@
 # format=tagmanager
-Common Greek letters�65536�0
+Common Greek letters�65536�Special Symbols�0
 Equations�64�0
 Figures�64�0
 Introduction�64�0
 Lists�64�0
 Literal text�64�0
 Special Symbols�64�0
-Special symbols�65536�0
+Special symbols�65536�Special Symbols�0
 Tables�64�0
-Test for ctags�16384�0
-\color{red�64�0
-\label{morefig�64�0
+Test for ctags�16384�Special Symbols""Common Greek letters�0
+\color{red}Use of Color�64�0
+\label{morefig}Subfigures�64�0
+\lb�16�0
+\rb�16�0
+\rv�16�0
 align�1�0
 cases�1�0
 center�1�0
@@ -26,14 +29,12 @@ fig:qm/complexfunctions
 fig:typical�2048�0
 figure�1�0
 itemize�1�0
-lb�16�0
-morefig�2048�0
+latex�8�0
 pmatrix�1�0
-rb�16�0
-rv�16�0
 subequations�1�0
 tab:5/tc�2048�0
 table�1�0
 tabular�1�0
 thebibliography�1�0
 verbatim�1�0
+website�8�0


Modified: tests/ctags/intro.tex.tags
18 lines changed, 11 insertions(+), 7 deletions(-)
===================================================================
@@ -1,10 +1,14 @@
 # format=tagmanager
-Introduction�64�0
+Introduction�64�chapter text�0
+Part1�2�0
+Part2�2�0
 chapter text�256�0
-chapter2�256�0
-section1 text�64�0
-section4 text�64�0
-subsection2�65536�0
-subsubsection3 with extra text�16384�0
-subsubsection6 with extra text�16384�0
+chapter2�256�Part2�0
+section1 text�64�chapter text�0
+short section4�64�Part1�0
+shorter intro2�64�Part2�0
+subsec5 text�65536�Part2""shorter intro2�0
+subsection2�65536�Part1�0
+subsubsection3 with extra text�16384�Part1""subsection2�0
+subsubsection6 with extra text�16384�Part2""shorter intro2""subsec5 text�0
 verbatim�1�0


Modified: tests/ctags/intro_orig.tex
15 lines changed, 15 insertions(+), 0 deletions(-)
===================================================================
@@ -20,6 +20,21 @@
 \setlength{\marginparpush}{1.0cm}
 \setlength{\textwidth}{150mm}
 
+\newenvironment{boxed}
+    {\begin{center}
+    \begin{tabular}{|p{0.9\textwidth}|}
+    \hline\\
+    }
+    { 
+    \\\\\hline
+    \end{tabular} 
+    \end{center}
+    }
+
+\DeclareMathOperator{\End}{End}
+
+\newtheorem{theorem1}{Theorem}
+
 \begin{comment}
 \pagestyle{empty} % use if page numbers not wanted
 \end{comment}


Modified: tests/ctags/intro_orig.tex.tags
20 lines changed, 12 insertions(+), 8 deletions(-)
===================================================================
@@ -1,16 +1,21 @@
 # format=tagmanager
-Common Greek letters�65536�0
+Common Greek letters�65536�Special Symbols�0
 Equations�64�0
 Figures�64�0
 Introduction�64�0
 Lists�64�0
 Literal text�64�0
 Special Symbols�64�0
-Special symbols�65536�0
+Special symbols�65536�Special Symbols�0
 Tables�64�0
-\color{red�64�0
-\label{morefig�64�0
+\End�16�0
+\color{red}Use of Color�64�0
+\label{morefig}Subfigures�64�0
+\lb�16�0
+\rb�16�0
+\rv�16�0
 align�1�0
+boxed�1�0
 cases�1�0
 center�1�0
 comment�1�0
@@ -25,14 +30,13 @@ fig:qm/complexfunctions
 fig:typical�2048�0
 figure�1�0
 itemize�1�0
-lb�16�0
-morefig�2048�0
+latex�8�0
 pmatrix�1�0
-rb�16�0
-rv�16�0
 subequations�1�0
 tab:5/tc�2048�0
 table�1�0
 tabular�1�0
 thebibliography�1�0
+theorem1�1�0
 verbatim�1�0
+website�8�0



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list