[geany/geany-plugins] ff95f4: Merge pull request #1098 from techee/geany_ctags_fix

Frank Lanitz git-noreply at xxxxx
Wed Sep 29 17:33:28 UTC 2021


Branch:      refs/heads/master
Author:      Frank Lanitz <frank at frank.uvena.de>
Committer:   GitHub <noreply at github.com>
Date:        Wed, 29 Sep 2021 17:33:28 UTC
Commit:      ff95f4b4286ff46dab24c0c00dc1d97f501effd5
             https://github.com/geany/geany-plugins/commit/ff95f4b4286ff46dab24c0c00dc1d97f501effd5

Log Message:
-----------
Merge pull request #1098 from techee/geany_ctags_fix

Geany ctags fixes


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

Modified: geanyctags/src/geanyctags.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -183,7 +183,7 @@ static gchar *generate_find_string(GeanyProject *prj)
 {
 	gchar *ret;
 
-	ret = g_strdup("find -L . -not -path '*/\\.*'");
+	ret = g_strdup("find -L . -not -path '*/\\.*' -type f");
 
 	if (!EMPTY(prj->file_patterns))
 	{


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