[geany/geany-plugins] 51c8ef: geanyctags: Update readtags.c/h to the latest version from uctags

Jiří Techet git-noreply at xxxxx
Wed Sep 29 17:33:33 UTC 2021


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Fri, 13 Aug 2021 20:30:55 UTC
Commit:      51c8ef9dd99c2d80a7c114506ab77739074992db
             https://github.com/geany/geany-plugins/commit/51c8ef9dd99c2d80a7c114506ab77739074992db

Log Message:
-----------
geanyctags: Update readtags.c/h to the latest version from uctags

There haven't been any reported problems with reading uctags tag files
with geanyctags but since uctags may add more stuff into the output,
this should make sure we are able to read such files.


Modified Paths:
--------------
    geanyctags/src/readtags.c
    geanyctags/src/readtags.h

Modified: geanyctags/src/readtags.c
990 lines changed, 660 insertions(+), 330 deletions(-)
===================================================================
@@ -1,6 +1,4 @@
 /*
-*   $Id: readtags.c 592 2007-07-31 03:30:41Z dhiebert $
-*
 *   Copyright (c) 1996-2003, Darren Hiebert
 *
 *   This source code is released into the public domain.
@@ -41,7 +39,7 @@ struct sTagFile {
 		/* format of tag file */
 	short format;
 		/* how is the tag file sorted? */
-	sortType sortMethod;
+	tagSortType sortMethod;
 		/* pointer to file structure */
 	FILE* fp;
 		/* file position of first character of `line' */
@@ -55,12 +53,12 @@ struct sTagFile {
 		/* defines tag search state */
 	struct {
 				/* file position of last match for tag */
-			off_t pos; 
+			off_t pos;
 				/* name of tag last searched for */
 			char *name;
 				/* length of name for partial matches */
 			size_t nameLength;
-				/* peforming partial match */
+				/* performing partial match */
 			short partial;
 				/* ignoring case */
 			short ignorecase;
@@ -83,55 +81,149 @@ struct sTagFile {
 			/* program version */
 		char *version;
 	} program;
+		/* 0 (initial state set by calloc), errno value,
+		 * or tagErrno typed value */
+	int err;
 };
 
 /*
 *   DATA DEFINITIONS
 */
-const char *const EmptyString = "";
-const char *const PseudoTagPrefix = "!_";
+static const char *const EmptyString = "";
+static const char *const PseudoTagPrefix = "!_";
+static const size_t PseudoTagPrefixLength = 2;
 
 /*
 *   FUNCTION DEFINITIONS
 */
 
+/* Converts a hexadecimal digit to its value */
+static int xdigitValue (char digit)
+{
+	if (digit >= '0' && digit <= '9')
+		return digit - '0';
+	else if (digit >= 'a' && digit <= 'f')
+		return 10 + digit - 'a';
+	else if (digit >= 'A' && digit <= 'F')
+		return 10 + digit - 'A';
+	else
+		return 0;
+}
+
+/*
+ * Reads the first character from the string, possibly un-escaping it, and
+ * advances *s to the start of the next character.
+ */
+static int readTagCharacter (const char **s)
+{
+	int c = **(const unsigned char **)s;
+
+	(*s)++;
+
+	if (c == '\\')
+	{
+		switch (**s)
+		{
+			case 't': c = '\t'; (*s)++; break;
+			case 'r': c = '\r'; (*s)++; break;
+			case 'n': c = '\n'; (*s)++; break;
+			case '\\': c = '\\'; (*s)++; break;
+			/* Universal-CTags extensions */
+			case 'a': c = '\a'; (*s)++; break;
+			case 'b': c = '\b'; (*s)++; break;
+			case 'v': c = '\v'; (*s)++; break;
+			case 'f': c = '\f'; (*s)++; break;
+			case 'x':
+				if (isxdigit ((*s)[1]) && isxdigit ((*s)[2]))
+				{
+					int val = (xdigitValue ((*s)[1]) << 4) | xdigitValue ((*s)[2]);
+					if (val < 0x80)
+					{
+						(*s) += 3;
+						c = val;
+					}
+				}
+				break;
+		}
+	}
+
+	return c;
+}
+
 /*
  * 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 !)
  */
-static int struppercmp (const char *s1, const char *s2)
+static int taguppercmp (const char *s1, const char *s2)
 {
 	int result;
+	int c1, c2;
 	do
 	{
-		result = toupper ((int) *s1) - toupper ((int) *s2);
-	} while (result == 0  &&  *s1++ != '\0'  &&  *s2++ != '\0');
+		c1 = (unsigned char)*s1++;
+		c2 = readTagCharacter (&s2);
+
+		result = toupper (c1) - toupper (c2);
+	} while (result == 0  &&  c1 != '\0'  &&  c2 != '\0');
 	return result;
 }
 
-static int strnuppercmp (const char *s1, const char *s2, size_t n)
+static int tagnuppercmp (const char *s1, const char *s2, size_t n)
 {
 	int result;
+	int c1, c2;
 	do
 	{
-		result = toupper ((int) *s1) - toupper ((int) *s2);
-	} while (result == 0  &&  --n > 0  &&  *s1++ != '\0'  &&  *s2++ != '\0');
+		c1 = (unsigned char)*s1++;
+		c2 = readTagCharacter (&s2);
+
+		result = toupper (c1) - toupper (c2);
+	} while (result == 0  &&  --n > 0  &&  c1 != '\0'  &&  c2 != '\0');
+	return result;
+}
+
+static int tagcmp (const char *s1, const char *s2)
+{
+	int result;
+	int c1, c2;
+	do
+	{
+		c1 = (unsigned char)*s1++;
+		c2 = readTagCharacter (&s2);
+
+		result = c1 - c2;
+	} while (result == 0  &&  c1 != '\0'  &&  c2 != '\0');
 	return result;
 }
 
-static int growString (vstring *s)
+static int tagncmp (const char *s1, const char *s2, size_t n)
 {
-	int result = 0;
+	int result;
+	int c1, c2;
+	do
+	{
+		c1 = *s1++;
+		c2 = readTagCharacter (&s2);
+
+		result = c1 - c2;
+	} while (result == 0  &&  --n > 0  &&  c1 != '\0'  &&  c2 != '\0');
+	return result;
+}
+
+static tagResult growString (vstring *s)
+{
+	tagResult result = TagFailure;
 	size_t newLength;
 	char *newLine;
 	if (s->size == 0)
 	{
 		newLength = 128;
 		newLine = (char*) malloc (newLength);
-		*newLine = '\0';
+		if (newLine)
+			*newLine = '\0';
 	}
 	else
 	{
@@ -144,13 +236,13 @@ static int growString (vstring *s)
 	{
 		s->buffer = newLine;
 		s->size = newLength;
-		result = 1;
+		result = TagSuccess;
 	}
 	return result;
 }
 
 /* Copy name of tag out of tag line */
-static void copyName (tagFile *const file)
+static tagResult copyName (tagFile *const file)
 {
 	size_t length;
 	const char *end = strchr (file->line.buffer, '\t');
@@ -165,12 +257,20 @@ static void copyName (tagFile *const file)
 	else
 		length = strlen (file->line.buffer);
 	while (length >= file->name.size)
-		growString (&file->name);
+	{
+		if (growString (&file->name) != TagSuccess)
+			return TagFailure;
+	}
 	strncpy (file->name.buffer, file->line.buffer, length);
 	file->name.buffer [length] = '\0';
+	return TagSuccess;
 }
 
-static int readTagLineRaw (tagFile *const file)
+/* Return 1 on success.
+ * Return 0 on failure or EOF.
+ * errno is set to *err unless EOF.
+ */
+static int readTagLineRaw (tagFile *const file, int *err)
 {
 	int result = 1;
 	int reReadLine;
@@ -186,22 +286,37 @@ static int readTagLineRaw (tagFile *const file)
 		char *line;
 
 		file->pos = ftell (file->fp);
+		if (file->pos < 0)
+		{
+			*err = errno;
+			result = 0;
+			break;
+		}
 		reReadLine = 0;
 		*pLastChar = '\0';
 		line = fgets (file->line.buffer, (int) file->line.size, file->fp);
 		if (line == NULL)
 		{
 			/* read error */
+			*err = 0;
 			if (! feof (file->fp))
-				perror ("readTagLine");
+				*err = errno;
 			result = 0;
 		}
 		else if (*pLastChar != '\0'  &&
 					*pLastChar != '\n'  &&  *pLastChar != '\r')
 		{
 			/*  buffer overflow */
-			growString (&file->line);
-			fseek (file->fp, file->pos, SEEK_SET);
+			if (growString (&file->line) != TagSuccess)
+			{
+				*err = ENOMEM;
+				result = 0;
+			}
+			if (fseek (file->fp, file->pos, SEEK_SET) < 0)
+			{
+				*err = errno;
+				result = 0;
+			}
 			reReadLine = 1;
 		}
 		else
@@ -216,16 +331,26 @@ static int readTagLineRaw (tagFile *const file)
 		}
 	} while (reReadLine  &&  result);
 	if (result)
-		copyName (file);
+	{
+		if (copyName (file) != TagSuccess)
+		{
+			*err = ENOMEM;
+			result = 0;
+		}
+	}
 	return result;
 }
 
-static int readTagLine (tagFile *const file)
+/* Return 1 on success.
+ * Return 0 on failure or EOF.
+ * errno is set to *err unless EOF.
+ */
+static int readTagLine (tagFile *const file, int *err)
 {
 	int result;
 	do
 	{
-		result = readTagLineRaw (file);
+		result = readTagLineRaw (file, err);
 	} while (result && *file->name.buffer == '\0');
 	return result;
 }
@@ -247,10 +372,13 @@ static tagResult growFields (tagFile *const file)
 	return result;
 }
 
-static void parseExtensionFields (tagFile *const file, tagEntry *const entry,
-								  char *const string)
+static tagResult parseExtensionFields (tagFile *const file, tagEntry *const entry,
+									   char *const string, int *err)
 {
 	char *p = string;
+	char *tail = string + (string? strlen(string):0);
+	size_t q_len;
+
 	while (p != NULL  &&  *p != '\0')
 	{
 		while (*p == TAB)
@@ -268,48 +396,142 @@ static void parseExtensionFields (tagFile *const file, tagEntry *const entry,
 			else
 			{
 				const char *key = field;
-				const char *value = colon + 1;
+				char *q = colon + 1;
+				const char *value = q;
+				const int key_len = colon - key;
 				*colon = '\0';
-				if (strcmp (key, "kind") == 0)
-					entry->kind = value;
-				else if (strcmp (key, "file") == 0)
-					entry->fileScope = 1;
-				else if (strcmp (key, "line") == 0)
-					entry->address.lineNumber = atol (value);
+
+				q_len = tail - q;
+
+				/* Unescaping */
+				while (*q != '\0')
+				{
+					const char *next = q;
+					int ch = readTagCharacter (&next);
+					size_t skip = next - q;
+
+					*q = (char) ch;
+					q++;
+					q_len -= skip;
+					if (skip > 1)
+					{
+						/* + 1 is for moving the area including the last '\0'. */
+						memmove (q, next, q_len + 1);
+						if (p)
+							p -= skip - 1;
+						if (tail != string)
+							tail -= skip - 1;
+					}
+				}
+
+				if (key_len == 4)
+				{
+					if (memcmp (key, "kind", 4) == 0)
+						entry->kind = value;
+					else if (memcmp (key, "file", 4) == 0)
+						entry->fileScope = 1;
+					else if (memcmp (key, "line", 4) == 0)
+					{
+						char *endptr = NULL;
+						long m = strtol (value, &endptr, 10);
+						if (*endptr != '\0' || m < 0)
+						{
+							*err = TagErrnoUnexpectedLineno;
+							return TagFailure;
+						}
+						entry->address.lineNumber = m;
+					}
+					else
+						goto normalField;
+				}
 				else
 				{
+				normalField:
 					if (entry->fields.count == file->fields.max)
-						growFields (file);
+					{
+						if (growFields (file) != TagSuccess)
+						{
+							*err = ENOMEM;
+							return TagFailure;
+						}
+					}
 					file->fields.list [entry->fields.count].key = key;
 					file->fields.list [entry->fields.count].value = value;
 					++entry->fields.count;
 				}
 			}
 		}
 	}
+	return TagSuccess;
+}
+
+static int isOdd (unsigned int i)
+{
+	return  (i % 2);
+}
+
+static unsigned int countContinuousBackslashesBackward(const char *from,
+						     const char *till)
+{
+	unsigned int counter = 0;
+
+	for (; from > till; from--)
+	{
+		if (*from == '\\')
+			counter++;
+		else
+			break;
+	}
+	return counter;
 }
 
-static void parseTagLine (tagFile *file, tagEntry *const entry)
+static tagResult parseTagLine (tagFile *file, tagEntry *const entry, int *err)
 {
 	int i;
 	char *p = file->line.buffer;
+	size_t p_len = strlen (p);
 	char *tab = strchr (p, TAB);
 
-	entry->fields.list = NULL;
-	entry->fields.count = 0;
-	entry->kind = NULL;
-	entry->fileScope = 0;
+	memset(entry, 0, sizeof(*entry));
 
 	entry->name = p;
 	if (tab != NULL)
 	{
 		*tab = '\0';
+	}
+
+	/* When unescaping, the input string becomes shorter.
+	 * e.g. \t occupies two bytes on the tag file.
+	 * It is converted to 0x9 and occupies one byte.
+	 * memmove called here for shortening the line
+	 * buffer. */
+	while (*p != '\0')
+	{
+		const char *next = p;
+		int ch = readTagCharacter (&next);
+		size_t skip = next - p;
+
+		*p = (char) ch;
+		p++;
+		p_len -= skip;
+		if (skip > 1)
+		{
+			/* + 1 is for moving the area including the last '\0'. */
+			memmove (p, next, p_len + 1);
+			if (tab)
+				tab -= skip - 1;
+		}
+	}
+
+	if (tab != NULL)
+	{
 		p = tab + 1;
 		entry->file = p;
 		tab = strchr (p, TAB);
 		if (tab != NULL)
 		{
 			int fieldsPresent;
+			int combinedPattern;
 			*tab = '\0';
 			p = tab + 1;
 			if (*p == '/'  ||  *p == '?')
@@ -321,10 +543,13 @@ static void parseTagLine (tagFile *file, tagEntry *const entry)
 				do
 				{
 					p = strchr (p + 1, delimiter);
-				} while (p != NULL  &&  *(p - 1) == '\\');
+				} while (p != NULL
+					 &&  isOdd (countContinuousBackslashesBackward (p - 1,
+											entry->address.pattern)));
+
 				if (p == NULL)
 				{
-					/* invalid pattern */
+					/* TODO: invalid pattern */
 				}
 				else
 					++p;
@@ -336,15 +561,46 @@ static void parseTagLine (tagFile *file, tagEntry *const entry)
 				entry->address.lineNumber = atol (p);
 				while (isdigit ((int) *(unsigned char*) p))
 					++p;
+				if (p)
+				{
+					combinedPattern = (strncmp (p, ";/", 2) == 0) ||
+											(strncmp (p, ";?", 2) == 0);
+					if (combinedPattern)
+					{
+						++p;
+						/* parse pattern */
+						int delimiter = *(unsigned char*) p;
+						do
+						{
+							p = strchr (p + 1, delimiter);
+						} while (p != NULL
+							 &&  isOdd (countContinuousBackslashesBackward (p - 1,
+													entry->address.pattern)));
+
+						if (p == NULL)
+						{
+							/* TODO: invalid pattern */
+						}
+						else
+							++p;
+					}
+				}
 			}
 			else
 			{
-				/* invalid pattern */
+				/* TODO: invalid pattern */
+			}
+
+			if (p)
+			{
+				fieldsPresent = (strncmp (p, ";\"", 2) == 0);
+				*p = '\0';
+				if (fieldsPresent)
+				{
+					if (parseExtensionFields (file, entry, p + 2, err) != TagSuccess)
+						return TagFailure;
+				}
 			}
-			fieldsPresent = (strncmp (p, ";\"", 2) == 0);
-			*p = '\0';
-			if (fieldsPresent)
-				parseExtensionFields (file, entry, p + 2);
 		}
 	}
 	if (entry->fields.count > 0)
@@ -354,6 +610,7 @@ static void parseTagLine (tagFile *file, tagEntry *const entry)
 		file->fields.list [i].key = NULL;
 		file->fields.list [i].value = NULL;
 	}
+	return TagSuccess;
 }
 
 static char *duplicate (const char *str)
@@ -368,103 +625,222 @@ static char *duplicate (const char *str)
 	return result;
 }
 
-static void readPseudoTags (tagFile *const file, tagFileInfo *const info)
+static int isPseudoTagLine (const char *buffer)
+{
+	return (strncmp (buffer, PseudoTagPrefix, PseudoTagPrefixLength) == 0);
+}
+
+static tagResult readPseudoTags (tagFile *const file, tagFileInfo *const info)
 {
 	fpos_t startOfLine;
+	int err = 0;
+	tagResult result = TagSuccess;
 	const size_t prefixLength = strlen (PseudoTagPrefix);
-	if (info != NULL)
-	{
-		info->file.format     = 1;
-		info->file.sort       = TAG_UNSORTED;
-		info->program.author  = NULL;
-		info->program.name    = NULL;
-		info->program.url     = NULL;
-		info->program.version = NULL;
-	}
+
+	info->file.format     = 1;
+	info->file.sort       = TAG_UNSORTED;
+	info->program.author  = NULL;
+	info->program.name    = NULL;
+	info->program.url     = NULL;
+	info->program.version = NULL;
+
 	while (1)
 	{
-		fgetpos (file->fp, &startOfLine);
-		if (! readTagLine (file))
+		if (fgetpos (file->fp, &startOfLine) < 0)
+		{
+			err = errno;
+			break;
+		}
+		if (! readTagLine (file, &err))
 			break;
-		if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
+		if (!isPseudoTagLine (file->line.buffer))
 			break;
 		else
 		{
 			tagEntry entry;
 			const char *key, *value;
-			parseTagLine (file, &entry);
+			if (parseTagLine (file, &entry, &err) != TagSuccess)
+				break;
 			key = entry.name + prefixLength;
 			value = entry.file;
 			if (strcmp (key, "TAG_FILE_SORTED") == 0)
-				file->sortMethod = (sortType) atoi (value);
+			{
+				char *endptr = NULL;
+				long m = strtol (value, &endptr, 10);
+				if (*endptr != '\0' || m < 0 || m > 2)
+				{
+					err = TagErrnoUnexpectedSortedMethod;
+					break;
+				}
+				file->sortMethod = (tagSortType) m;
+			}
 			else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
-				file->format = (short) atoi (value);
+			{
+				char *endptr = NULL;
+				long m = strtol (value, &endptr, 10);
+				if (*endptr != '\0' || m < 1 || m > 2)
+				{
+					err = TagErrnoUnexpectedFormat;
+					break;
+				}
+				file->format = (short) m;
+			}
 			else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
+			{
 				file->program.author = duplicate (value);
+				if (value && file->program.author == NULL)
+				{
+					err = ENOMEM;
+					break;
+				}
+			}
 			else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
+			{
 				file->program.name = duplicate (value);
+				if (value && file->program.name == NULL)
+				{
+					err = ENOMEM;
+					break;
+				}
+			}
 			else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
+			{
 				file->program.url = duplicate (value);
+				if (value && file->program.url == NULL)
+				{
+					err = ENOMEM;
+					break;
+				}
+			}
 			else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
-				file->program.version = duplicate (value);
-			if (info != NULL)
 			{
-				info->file.format     = file->format;
-				info->file.sort       = file->sortMethod;
-				info->program.author  = file->program.author;
-				info->program.name    = file->program.name;
-				info->program.url     = file->program.url;
-				info->program.version = file->program.version;
+				file->program.version = duplicate (value);
+				if (value && file->program.version == NULL)
+				{
+					err = ENOMEM;
+					break;
+				}
 			}
+
+			info->file.format     = file->format;
+			info->file.sort       = file->sortMethod;
+			info->program.author  = file->program.author;
+			info->program.name    = file->program.name;
+			info->program.url     = file->program.url;
+			info->program.version = file->program.version;
 		}
 	}
-	fsetpos (file->fp, &startOfLine);
+	if (fsetpos (file->fp, &startOfLine) < 0)
+		err = errno;
+
+	info->status.error_number = err;
+	if (err)
+		result = TagFailure;
+	return result;
+}
+
+static int doesFilePointPseudoTag (tagFile *const file, void *unused)
+{
+	return isPseudoTagLine (file->name.buffer);
 }
 
-static void gotoFirstLogicalTag (tagFile *const file)
+static tagResult gotoFirstLogicalTag (tagFile *const file)
 {
 	fpos_t startOfLine;
-	const size_t prefixLength = strlen (PseudoTagPrefix);
-	rewind (file->fp);
+
+	if (fseek(file->fp, 0L, SEEK_SET) == -1)
+	{
+		file->err = errno;
+		return TagFailure;
+	}
+
 	while (1)
 	{
-		fgetpos (file->fp, &startOfLine);
-		if (! readTagLine (file))
+		if (fgetpos (file->fp, &startOfLine) < 0)
+		{
+			file->err = errno;
+			return TagFailure;
+		}
+		if (! readTagLine (file, &file->err))
+		{
+			if (file->err)
+				return TagFailure;
 			break;
-		if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
+		}
+		if (!isPseudoTagLine (file->line.buffer))
 			break;
 	}
-	fsetpos (file->fp, &startOfLine);
+	if (fsetpos (file->fp, &startOfLine) < 0)
+	{
+		file->err = errno;
+		return TagFailure;
+	}
+	return TagSuccess;
 }
 
 static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
 {
 	tagFile *result = (tagFile*) calloc ((size_t) 1, sizeof (tagFile));
-	if (result != NULL)
-	{
-		growString (&result->line);
-		growString (&result->name);
-		result->fields.max = 20;
-		result->fields.list = (tagExtensionField*) calloc (
-			result->fields.max, sizeof (tagExtensionField));
-		result->fp = fopen (filePath, "r");
-		if (result->fp == NULL)
+
+	if (result == NULL)
+	{
+		info->status.opened = 0;
+		info->status.error_number = ENOMEM;
+		return NULL;
+	}
+
+	if (growString (&result->line) != TagSuccess)
+		goto mem_error;
+	if (growString (&result->name) != TagSuccess)
+		goto mem_error;
+	result->fields.max = 20;
+	result->fields.list = (tagExtensionField*) calloc (
+		result->fields.max, sizeof (tagExtensionField));
+	if (result->fields.list == NULL)
+		goto mem_error;
+	result->fp = fopen (filePath, "rb");
+	if (result->fp == NULL)
+	{
+		info->status.error_number = errno;
+		goto file_error;
+	}
+	else
+	{
+		if (fseek (result->fp, 0, SEEK_END) == -1)
 		{
-			free (result);
-			result = NULL;
 			info->status.error_number = errno;
+			goto file_error;
 		}
-		else
+		result->size = ftell (result->fp);
+		if (result->size == -1)
+		{
+			info->status.error_number = errno;
+			goto file_error;
+		}
+		if (fseek(result->fp, 0L, SEEK_SET) == -1)
 		{
-			fseek (result->fp, 0, SEEK_END);
-			result->size = ftell (result->fp);
-			rewind (result->fp);
-			readPseudoTags (result, info);
-			info->status.opened = 1;
-			result->initialized = 1;
+			info->status.error_number = errno;
+			goto file_error;
 		}
+
+		if (readPseudoTags (result, info) == TagFailure)
+			goto file_error;
+
+		info->status.opened = 1;
+		result->initialized = 1;
 	}
 	return result;
+ mem_error:
+	info->status.error_number = ENOMEM;
+ file_error:
+	free (result->line.buffer);
+	free (result->name.buffer);
+	free (result->fields.list);
+	if (result->fp)
+		fclose (result->fp);
+	free (result);
+	info->status.opened = 0;
+	return NULL;
 }
 
 static void terminate (tagFile *const file)
@@ -495,14 +871,17 @@ static tagResult readNext (tagFile *const file, tagEntry *const entry)
 {
 	tagResult result;
 	if (file == NULL  ||  ! file->initialized)
+	{
+		file->err = TagErrnoInvalidArgument;
 		result = TagFailure;
-	else if (! readTagLine (file))
+	}
+	else if (! readTagLine (file, &file->err))
 		result = TagFailure;
 	else
 	{
-		if (entry != NULL)
-			parseTagLine (file, entry);
-		result = TagSuccess;
+		result = (entry != NULL)
+			? parseTagLine (file, entry, &file->err)
+			: TagSuccess;
 	}
 	return result;
 }
@@ -524,14 +903,21 @@ static const char *readFieldValue (
 
 static int readTagLineSeek (tagFile *const file, const off_t pos)
 {
-	int result = 0;
-	if (fseek (file->fp, pos, SEEK_SET) == 0)
+	if (fseek (file->fp, pos, SEEK_SET) < 0)
 	{
-		result = readTagLine (file);  /* read probable partial line */
-		if (pos > 0  &&  result)
-			result = readTagLine (file);  /* read complete line */
+		file->err = errno;
+		return 0;
 	}
-	return result;
+
+	/* read probable partial line */
+	if (!readTagLine (file, &file->err))
+		return 0;
+
+	/* read complete line */
+	if (pos > 0)
+		return readTagLine (file, &file->err);
+
+	return 1;
 }
 
 static int nameComparison (tagFile *const file)
@@ -540,23 +926,23 @@ static int nameComparison (tagFile *const file)
 	if (file->search.ignorecase)
 	{
 		if (file->search.partial)
-			result = strnuppercmp (file->search.name, file->name.buffer,
+			result = tagnuppercmp (file->search.name, file->name.buffer,
 					file->search.nameLength);
 		else
-			result = struppercmp (file->search.name, file->name.buffer);
+			result = taguppercmp (file->search.name, file->name.buffer);
 	}
 	else
 	{
 		if (file->search.partial)
-			result = strncmp (file->search.name, file->name.buffer,
+			result = tagncmp (file->search.name, file->name.buffer,
 					file->search.nameLength);
 		else
-			result = strcmp (file->search.name, file->name.buffer);
+			result = tagcmp (file->search.name, file->name.buffer);
 	}
 	return result;
 }
 
-static void findFirstNonMatchBefore (tagFile *const file)
+static tagResult findFirstNonMatchBefore (tagFile *const file)
 {
 #define JUMP_BACK 512
 	int more_lines;
@@ -570,19 +956,25 @@ static void findFirstNonMatchBefore (tagFile *const file)
 		else
 			pos = pos - JUMP_BACK;
 		more_lines = readTagLineSeek (file, pos);
+		if (more_lines == 0 && file->err)
+			return TagFailure;
 		comp = nameComparison (file);
 	} while (more_lines  &&  comp == 0  &&  pos > 0  &&  pos < start);
+	return TagSuccess;
 }
 
 static tagResult findFirstMatchBefore (tagFile *const file)
 {
 	tagResult result = TagFailure;
 	int more_lines;
 	off_t start = file->pos;
-	findFirstNonMatchBefore (file);
+	if (findFirstNonMatchBefore (file) != TagSuccess)
+		return TagFailure;
 	do
 	{
-		more_lines = readTagLine (file);
+		more_lines = readTagLine (file, &file->err);
+		if (more_lines == 0 && file->err)
+			return TagFailure;
 		if (nameComparison (file) == 0)
 			result = TagSuccess;
 	} while (more_lines  &&  result != TagSuccess  &&  file->pos < start);
@@ -600,6 +992,8 @@ static tagResult findBinary (tagFile *const file)
 	{
 		if (! readTagLineSeek (file, pos))
 		{
+			if (file->err)
+				break;
 			/* in case we fell off end of file */
 			result = findFirstMatchBefore (file);
 			break;
@@ -626,122 +1020,215 @@ static tagResult findBinary (tagFile *const file)
 			else if (pos == 0)
 				result = TagSuccess;
 			else
+			{
 				result = findFirstMatchBefore (file);
+				if (result != TagSuccess && file->err)
+					break;
+			}
 		}
 	}
 	return result;
 }
 
-static tagResult findSequential (tagFile *const file)
+static tagResult findSequentialFull (tagFile *const file,
+									 int (* isAcceptable) (tagFile *const, void *),
+									 void *data)
 {
+	if (file == NULL || !file->initialized || file->err)
+	{
+		file->err = TagErrnoInvalidArgument;
+		return TagFailure;
+	}
+
 	tagResult result = TagFailure;
-	if (file->initialized)
+	while (result == TagFailure)
 	{
-		while (result == TagFailure  &&  readTagLine (file))
-		{
-			if (nameComparison (file) == 0)
-				result = TagSuccess;
-		}
+		if (! readTagLine (file, &file->err))
+			break;
+		if (isAcceptable (file, data))
+			result = TagSuccess;
 	}
 	return result;
 }
 
+static int nameAcceptable (tagFile *const file, void *unused)
+{
+	return (nameComparison (file) == 0);
+}
+
+static tagResult findSequential (tagFile *const file)
+{
+	return findSequentialFull (file, nameAcceptable, NULL);
+}
+
 static tagResult find (tagFile *const file, tagEntry *const entry,
 					   const char *const name, const int options)
 {
 	tagResult result;
 	if (file->search.name != NULL)
 		free (file->search.name);
 	file->search.name = duplicate (name);
+	if (file->search.name == NULL)
+	{
+		file->err = ENOMEM;
+		return TagFailure;
+	}
 	file->search.nameLength = strlen (name);
 	file->search.partial = (options & TAG_PARTIALMATCH) != 0;
 	file->search.ignorecase = (options & TAG_IGNORECASE) != 0;
-	fseek (file->fp, 0, SEEK_END);
+	if (fseek (file->fp, 0, SEEK_END) < 0)
+	{
+		file->err = errno;
+		return TagFailure;
+	}
 	file->size = ftell (file->fp);
-	rewind (file->fp);
+	if (file->size == -1)
+	{
+		file->err = errno;
+		return TagFailure;
+	}
+	if (fseek(file->fp, 0L, SEEK_SET) == -1)
+	{
+		file->err = errno;
+		return TagFailure;
+	}
 	if ((file->sortMethod == TAG_SORTED      && !file->search.ignorecase) ||
 		(file->sortMethod == TAG_FOLDSORTED  &&  file->search.ignorecase))
 	{
 #ifdef DEBUG
-		printf ("<performing binary search>\n");
+		fputs ("<performing binary search>\n", stderr);
 #endif
 		result = findBinary (file);
+		if (result == TagFailure && file->err)
+			return TagFailure;
 	}
 	else
 	{
 #ifdef DEBUG
-		printf ("<performing sequential search>\n");
+		fputs ("<performing sequential search>\n", stderr);
 #endif
 		result = findSequential (file);
+		if (result == TagFailure && file->err)
+			return TagFailure;
 	}
 
 	if (result != TagSuccess)
 		file->search.pos = file->size;
 	else
 	{
 		file->search.pos = file->pos;
-		if (entry != NULL)
-			parseTagLine (file, entry);
+		result = (entry != NULL)
+			? parseTagLine (file, entry, &file->err)
+			: TagSuccess;
 	}
 	return result;
 }
 
-static tagResult findNext (tagFile *const file, tagEntry *const entry)
+static tagResult findNextFull (tagFile *const file, tagEntry *const entry,
+							   int sorted,
+							   int (* isAcceptable) (tagFile *const, void *),
+							   void *data)
 {
 	tagResult result;
-	if ((file->sortMethod == TAG_SORTED      && !file->search.ignorecase) ||
-		(file->sortMethod == TAG_FOLDSORTED  &&  file->search.ignorecase))
+	if (sorted)
 	{
 		result = tagsNext (file, entry);
-		if (result == TagSuccess  && nameComparison (file) != 0)
+		if (result == TagSuccess  && !isAcceptable (file, data))
 			result = TagFailure;
 	}
 	else
 	{
-		result = findSequential (file);
+		result = findSequentialFull (file, isAcceptable, data);
 		if (result == TagSuccess  &&  entry != NULL)
-			parseTagLine (file, entry);
+			result = parseTagLine (file, entry, &file->err);
 	}
 	return result;
 }
 
+static tagResult findNext (tagFile *const file, tagEntry *const entry)
+{
+	return findNextFull (file, entry,
+						 (file->sortMethod == TAG_SORTED      && !file->search.ignorecase) ||
+						 (file->sortMethod == TAG_FOLDSORTED  &&  file->search.ignorecase),
+						 nameAcceptable, NULL);
+}
+
+static tagResult findPseudoTag (tagFile *const file, int rewindBeforeFinding, tagEntry *const entry)
+{
+	if (file == NULL || (!file->initialized) || file->err)
+	{
+		file->err= TagErrnoInvalidArgument;;
+		return TagFailure;
+	}
+
+	if (rewindBeforeFinding)
+	{
+		if (fseek(file->fp, 0L, SEEK_SET) == -1)
+		{
+			file->err = errno;
+			return TagFailure;
+		}
+	}
+	return findNextFull (file, entry,
+						 (file->sortMethod == TAG_SORTED || file->sortMethod == TAG_FOLDSORTED),
+						 doesFilePointPseudoTag,
+						 NULL);
+}
+
+
 /*
 *  EXTERNAL INTERFACE
 */
 
 extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info)
 {
-	return initialize (filePath, info);
+	tagFileInfo infoDummy;
+	return initialize (filePath, info? info: &infoDummy);
 }
 
-extern tagResult tagsSetSortType (tagFile *const file, const sortType type)
+extern tagResult tagsSetSortType (tagFile *const file, const tagSortType type)
 {
-	tagResult result = TagFailure;
-	if (file != NULL  &&  file->initialized)
+	if (file == NULL || (!file->initialized) || file->err)
 	{
+		file->err = TagErrnoInvalidArgument;
+		return TagFailure;
+	}
+
+	switch (type)
+	{
+	case TAG_UNSORTED:
+	case TAG_SORTED:
+	case TAG_FOLDSORTED:
 		file->sortMethod = type;
-		result = TagSuccess;
+		return TagSuccess;
+	default:
+		file->err = TagErrnoUnexpectedSortedMethod;
+		return TagFailure;
 	}
-	return result;
 }
 
 extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry)
 {
-	tagResult result = TagFailure;
-	if (file != NULL  &&  file->initialized)
+	if (file == NULL || (!file->initialized) || file->err)
 	{
-		gotoFirstLogicalTag (file);
-		result = readNext (file, entry);
+		file->err = TagErrnoInvalidArgument;
+		return TagFailure;
 	}
-	return result;
+
+	if (gotoFirstLogicalTag (file) != TagSuccess)
+		return TagFailure;
+	return readNext (file, entry);
 }
 
 extern tagResult tagsNext (tagFile *const file, tagEntry *const entry)
 {
-	tagResult result = TagFailure;
-	if (file != NULL  &&  file->initialized)
-		result = readNext (file, entry);
-	return result;
+	if (file == NULL || (!file->initialized) || file->err)
+	{
+		file->err = TagErrnoInvalidArgument;
+		return TagFailure;
+	}
+
+	return readNext (file, entry);
 }
 
 extern const char *tagsField (const tagEntry *const entry, const char *const key)
@@ -755,205 +1242,48 @@ extern const char *tagsField (const tagEntry *const entry, const char *const key
 extern tagResult tagsFind (tagFile *const file, tagEntry *const entry,
 						   const char *const name, const int options)
 {
-	tagResult result = TagFailure;
-	if (file != NULL  &&  file->initialized)
-		result = find (file, entry, name, options);
-	return result;
+	if (file == NULL || !file->initialized || file->err)
+	{
+		file->err = TagErrnoInvalidArgument;
+		return TagFailure;
+	}
+	return find (file, entry, name, options);
 }
 
 extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry)
 {
-	tagResult result = TagFailure;
-	if (file != NULL  &&  file->initialized)
-		result = findNext (file, entry);
-	return result;
-}
-
-extern tagResult tagsClose (tagFile *const file)
-{
-	tagResult result = TagFailure;
-	if (file != NULL  &&  file->initialized)
+	if (file == NULL || !file->initialized || file->err)
 	{
-		terminate (file);
-		result = TagSuccess;
+		file->err = TagErrnoInvalidArgument;
+		return TagFailure;
 	}
-	return result;
+	return findNext (file, entry);
 }
 
-/*
-*  TEST FRAMEWORK
-*/
-
-#ifdef READTAGS_MAIN
-
-static const char *TagFileName = "tags";
-static const char *ProgramName;
-static int extensionFields;
-static int SortOverride;
-static sortType SortMethod;
-
-static void printTag (const tagEntry *entry)
+extern tagResult tagsFirstPseudoTag (tagFile *const file, tagEntry *const entry)
 {
-	int i;
-	int first = 1;
-	const char* separator = ";\"";
-	const char* const empty = "";
-/* "sep" returns a value only the first time it is evaluated */
-#define sep (first ? (first = 0, separator) : empty)
-	printf ("%s\t%s\t%s",
-		entry->name, entry->file, entry->address.pattern);
-	if (extensionFields)
-	{
-		if (entry->kind != NULL  &&  entry->kind [0] != '\0')
-			printf ("%s\tkind:%s", sep, entry->kind);
-		if (entry->fileScope)
-			printf ("%s\tfile:", sep);
-#if 0
-		if (entry->address.lineNumber > 0)
-			printf ("%s\tline:%lu", sep, entry->address.lineNumber);
-#endif
-		for (i = 0  ;  i < entry->fields.count  ;  ++i)
-			printf ("%s\t%s:%s", sep, entry->fields.list [i].key,
-				entry->fields.list [i].value);
-	}
-	putchar ('\n');
-#undef sep
+	return findPseudoTag (file, 1, entry);
 }
 
-static void findTag (const char *const name, const int options)
+extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry)
 {
-	tagFileInfo info;
-	tagEntry entry;
-	tagFile *const file = tagsOpen (TagFileName, &info);
-	if (file == NULL)
-	{
-		fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
-				ProgramName, strerror (info.status.error_number), name);
-		exit (1);
-	}
-	else
-	{
-		if (SortOverride)
-			tagsSetSortType (file, SortMethod);
-		if (tagsFind (file, &entry, name, options) == TagSuccess)
-		{
-			do
-			{
-				printTag (&entry);
-			} while (tagsFindNext (file, &entry) == TagSuccess);
-		}
-		tagsClose (file);
-	}
+	return findPseudoTag (file, 0, entry);
 }
 
-static void listTags (void)
+extern tagResult tagsClose (tagFile *const file)
 {
-	tagFileInfo info;
-	tagEntry entry;
-	tagFile *const file = tagsOpen (TagFileName, &info);
-	if (file == NULL)
-	{
-		fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
-				ProgramName, strerror (info.status.error_number), TagFileName);
-		exit (1);
-	}
-	else
+	tagResult result = TagFailure;
+	if (file != NULL  &&  file->initialized)
 	{
-		while (tagsNext (file, &entry) == TagSuccess)
-			printTag (&entry);
-		tagsClose (file);
+		terminate (file);
+		result = TagSuccess;
 	}
+	return result;
 }
 
-const char *const Usage =
-	"Find tag file entries matching specified names.\n\n"
-	"Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n"
-	"Options:\n"
-	"    -e           Include extension fields in output.\n"
-	"    -i           Perform case-insensitive matching.\n"
-	"    -l           List all tags.\n"
-	"    -p           Perform partial matching.\n"
-	"    -s[0|1|2]    Override sort detection of tag file.\n"
-	"    -t file      Use specified tag file (default: \"tags\").\n"
-	"Note that options are acted upon as encountered, so order is significant.\n";
-
-extern int main (int argc, char **argv)
+extern int tagsGetErrno (tagFile *const file)
 {
-	int options = 0;
-	int actionSupplied = 0;
-	int i;
-	ProgramName = argv [0];
-	if (argc == 1)
-	{
-		fprintf (stderr, Usage, ProgramName);
-		exit (1);
-	}
-	for (i = 1  ;  i < argc  ;  ++i)
-	{
-		const char *const arg = argv [i];
-		if (arg [0] != '-')
-		{
-			findTag (arg, options);
-			actionSupplied = 1;
-		}
-		else
-		{
-			size_t j;
-			for (j = 1  ;  arg [j] != '\0'  ;  ++j)
-			{
-				switch (arg [j])
-				{
-					case 'e': extensionFields = 1;         break;
-					case 'i': options |= TAG_IGNORECASE;   break;
-					case 'p': options |= TAG_PARTIALMATCH; break;
-					case 'l': listTags (); actionSupplied = 1; break;
-			
-					case 't':
-						if (arg [j+1] != '\0')
-						{
-							TagFileName = arg + j + 1;
-							j += strlen (TagFileName);
-						}
-						else if (i + 1 < argc)
-							TagFileName = argv [++i];
-						else
-						{
-							fprintf (stderr, Usage, ProgramName);
-							exit (1);
-						}
-						break;
-					case 's':
-						SortOverride = 1;
-						++j;
-						if (arg [j] == '\0')
-							SortMethod = TAG_SORTED;
-						else if (strchr ("012", arg[j]) != NULL)
-							SortMethod = (sortType) (arg[j] - '0');
-						else
-						{
-							fprintf (stderr, Usage, ProgramName);
-							exit (1);
-						}
-						break;
-					default:
-						fprintf (stderr, "%s: unknown option: %c\n",
-									ProgramName, arg[j]);
-						exit (1);
-						break;
-				}
-			}
-		}
-	}
-	if (! actionSupplied)
-	{
-		fprintf (stderr,
-			"%s: no action specified: specify tag name(s) or -l option\n",
-			ProgramName);
-		exit (1);
-	}
-	return 0;
+	if (file == NULL)
+		return TagErrnoInvalidArgument;
+	return file->err;
 }
-
-#endif
-
-/* vi:set tabstop=4 shiftwidth=4: */


Modified: geanyctags/src/readtags.h
79 lines changed, 61 insertions(+), 18 deletions(-)
===================================================================
@@ -1,6 +1,4 @@
 /*
-*   $Id: readtags.h 443 2006-05-30 04:37:13Z darren $
-*
 *   Copyright (c) 1996-2003, Darren Hiebert
 *
 *   This source code is released for the public domain.
@@ -12,7 +10,7 @@
 *   support to a software tool. The tag lookups provided are sufficiently fast
 *   enough to permit opening a sorted tag file, searching for a matching tag,
 *   then closing the tag file each time a tag is looked up (search times are
-*   on the order of hundreths of a second, even for huge tag files). This is
+*   on the order of hundredths of a second, even for huge tag files). This is
 *   the recommended use of this library for most tool applications. Adhering
 *   to this approach permits a user to regenerate a tag file at will without
 *   the tool needing to detect and resynchronize with changes to the tag file.
@@ -32,7 +30,14 @@ extern "C" {
 /* Options for tagsSetSortType() */
 typedef enum {
 	TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED
-} sortType ;
+} tagSortType ;
+
+/* For source code level compatibility, sortType is defined here.
+*  Define TAG_NO_COMPAT_SORT_TYPE if you want to avoid namespace pollution.
+*/
+#ifndef TAG_NO_COMPAT_SORT_TYPE
+#define sortType tagSortType
+#endif
 
 /* Options for tagsFind() */
 #define TAG_FULLMATCH     0x0
@@ -47,6 +52,15 @@ typedef enum {
 
 typedef enum { TagFailure = 0, TagSuccess = 1 } tagResult;
 
+typedef enum {
+	TagErrnoUnexpectedSortedMethod = -1, /* Unexpected sorted method */
+	TagErrnoUnexpectedFormat       = -2, /* Unexpected format number */
+	TagErrnoUnexpectedLineno       = -3, /* Unexpected value for line: field
+										  * (Zero or a positive integer is expected.) */
+	TagErrnoInvalidArgument        = -4, /* Unexpected argument passed to the API
+										  * function */
+} tagErrno;
+
 struct sTagFile;
 
 typedef struct sTagFile tagFile;
@@ -58,7 +72,8 @@ typedef struct {
 			/* was the tag file successfully opened? */
 		int opened;
 
-			/* errno value when 'opened' is false */
+			/* errno value or tagErrno typed value
+			   when 'opened' is false */
 		int error_number;
 	} status;
 
@@ -68,7 +83,7 @@ typedef struct {
 			short format;
 
 				/* how is the tag file sorted? */
-			sortType sort;
+			tagSortType sort;
 	} file;
 
 
@@ -108,7 +123,8 @@ typedef struct {
 		/* name of tag */
 	const char *name;
 
-		/* path of source file containing definition of tag */
+		/* path of source file containing definition of tag.
+		   For a broken tags file, this can be NULL. */
 	const char *file;
 
 		/* address for locating tag in source file */
@@ -150,10 +166,14 @@ typedef struct {
 *  null) pointer to a structure which, if not null, will be populated with
 *  information about the tag file. If successful, the function will return a
 *  handle which must be supplied to other calls to read information from the
-*  tag file, and info.status.opened will be set to true. If unsuccessful,
-*  info.status.opened will be set to false and info.status.error_number will
-*  be set to the errno value representing the system error preventing the tag
-*  file from being successfully opened.
+*  tag file, and info.status.opened will be set to true.
+*  If unsuccessful, the function will return NULL, and
+*  info.status.opened will be set to false and
+*  info.status.error_number will be set to either the errno value
+*  representing the system error preventing the tag file from being
+*  successfully opened, or the tagErrno typed value representing the
+*  library level error. The error_number will be ENOMEM if the memory
+*  allocation for the handle is failed.
 */
 extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info);
 
@@ -170,7 +190,7 @@ extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info);
 *  it actually is not. The function will return TagSuccess if called on an
 *  open tag file or TagFailure if not.
 */
-extern tagResult tagsSetSortType (tagFile *const file, const sortType type);
+extern tagResult tagsSetSortType (tagFile *const file, const tagSortType type);
 
 /*
 *  Reads the first tag in the file, if any. It is passed the handle to an
@@ -215,11 +235,11 @@ extern const char *tagsField (const tagEntry *const entry, const char *const key
 *        Only tags whose full lengths match `name' will qualify.
 *
 *    TAG_IGNORECASE
-*        Matching will be performed in a case-insenstive manner. Note that
+*        Matching will be performed in a case-insensitive manner. Note that
 *        this disables binary searches of the tag file.
 *
 *    TAG_OBSERVECASE
-*        Matching will be performed in a case-senstive manner. Note that
+*        Matching will be performed in a case-sensitive manner. Note that
 *        this enables binary searches of the tag file.
 *
 *  The function will return TagSuccess if a tag matching the name is found, or
@@ -237,16 +257,39 @@ extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, const cha
 extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry);
 
 /*
-*  Call tagsTerminate() at completion of reading the tag file, which will
+*  Does the same as tagsFirst(), but is specialized to pseudo tags.
+*  If tagFileInfo doesn't contain pseudo tags you are interested, read
+*  them sequentially with this function and tagsNextPseudoTag().
+*/
+extern tagResult tagsFirstPseudoTag (tagFile *const file, tagEntry *const entry);
+
+/*
+*  Does the same as tagsNext(), but is specialized to pseudo tags. Use with
+*  tagsFirstPseudoTag().
+*/
+extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry);
+
+/*
+*  Call tagsClose() at completion of reading the tag file, which will
 *  close the file and free any internal memory allocated. The function will
-*  return TagFailure is no file is currently open, TagSuccess otherwise.
+*  return TagFailure if no file is currently open, TagSuccess otherwise.
 */
 extern tagResult tagsClose (tagFile *const file);
 
+/*
+*  Get the error status set in the last API call.
+*  Much of the API functions return TagFailure because (1) no tag is
+*  found, or (2) an error occurs. tagsGetErrno() is for distinguishing
+*  (1) or (2). This function will return 0 for (1). The errno value
+*  representing the system error or tagErrno value for (2).
+*
+*  This function does not deal with the results of tagsOpen(),
+*  tagsClose(), and tagsField().
+*/
+extern int tagsGetErrno (tagFile *const file);
+
 #ifdef __cplusplus
 };
 #endif
 
 #endif
-
-/* vi:set tabstop=4 shiftwidth=4: */



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


More information about the Plugins-Commits mailing list