xref: /Universal-ctags/libreadtags/readtags.c (revision d5b81c4aa18f31534f9ed152bfb0988c340b28ff)
14d9e7fc5SMasatake YAMATO /*
24d9e7fc5SMasatake YAMATO *   Copyright (c) 1996-2003, Darren Hiebert
34d9e7fc5SMasatake YAMATO *
44d9e7fc5SMasatake YAMATO *   This source code is released into the public domain.
54d9e7fc5SMasatake YAMATO *
64d9e7fc5SMasatake YAMATO *   This module contains functions for reading tag files.
74d9e7fc5SMasatake YAMATO */
84d9e7fc5SMasatake YAMATO 
94d9e7fc5SMasatake YAMATO /*
104d9e7fc5SMasatake YAMATO *   INCLUDE FILES
114d9e7fc5SMasatake YAMATO */
124d9e7fc5SMasatake YAMATO #include <stdlib.h>
134d9e7fc5SMasatake YAMATO #include <string.h>
144d9e7fc5SMasatake YAMATO #include <ctype.h>
154d9e7fc5SMasatake YAMATO #include <stdio.h>
164d9e7fc5SMasatake YAMATO #include <errno.h>
174d9e7fc5SMasatake YAMATO #include <sys/types.h>  /* to declare off_t */
184d9e7fc5SMasatake YAMATO 
194d9e7fc5SMasatake YAMATO #include "readtags.h"
204d9e7fc5SMasatake YAMATO 
214d9e7fc5SMasatake YAMATO /*
224d9e7fc5SMasatake YAMATO *   MACROS
234d9e7fc5SMasatake YAMATO */
244d9e7fc5SMasatake YAMATO #define TAB '\t'
254d9e7fc5SMasatake YAMATO 
264d9e7fc5SMasatake YAMATO 
274d9e7fc5SMasatake YAMATO /*
284d9e7fc5SMasatake YAMATO *   DATA DECLARATIONS
294d9e7fc5SMasatake YAMATO */
304d9e7fc5SMasatake YAMATO typedef struct {
314d9e7fc5SMasatake YAMATO 	size_t size;
324d9e7fc5SMasatake YAMATO 	char *buffer;
334d9e7fc5SMasatake YAMATO } vstring;
344d9e7fc5SMasatake YAMATO 
35*d5b81c4aSMasatake YAMATO /* Define readtags' own off_t. */
36*d5b81c4aSMasatake YAMATO #ifdef _WIN32
37*d5b81c4aSMasatake YAMATO typedef long long rt_off_t;
38*d5b81c4aSMasatake YAMATO #else
39*d5b81c4aSMasatake YAMATO typedef off_t rt_off_t;
40*d5b81c4aSMasatake YAMATO #endif
41*d5b81c4aSMasatake YAMATO 
424d9e7fc5SMasatake YAMATO /* Information about current tag file */
434d9e7fc5SMasatake YAMATO struct sTagFile {
444d9e7fc5SMasatake YAMATO 		/* has the file been opened and this structure initialized? */
454d9e7fc5SMasatake YAMATO 	short initialized;
464d9e7fc5SMasatake YAMATO 		/* format of tag file */
474d9e7fc5SMasatake YAMATO 	short format;
484d9e7fc5SMasatake YAMATO 		/* how is the tag file sorted? */
49a41b1651SMasatake YAMATO 	tagSortType sortMethod;
504d9e7fc5SMasatake YAMATO 		/* pointer to file structure */
514d9e7fc5SMasatake YAMATO 	FILE* fp;
524d9e7fc5SMasatake YAMATO 		/* file position of first character of `line' */
53*d5b81c4aSMasatake YAMATO 	rt_off_t pos;
544d9e7fc5SMasatake YAMATO 		/* size of tag file in seekable positions */
55*d5b81c4aSMasatake YAMATO 	rt_off_t size;
564d9e7fc5SMasatake YAMATO 		/* last line read */
574d9e7fc5SMasatake YAMATO 	vstring line;
584d9e7fc5SMasatake YAMATO 		/* name of tag in last line read */
594d9e7fc5SMasatake YAMATO 	vstring name;
604d9e7fc5SMasatake YAMATO 		/* defines tag search state */
614d9e7fc5SMasatake YAMATO 	struct {
624d9e7fc5SMasatake YAMATO 				/* file position of last match for tag */
63*d5b81c4aSMasatake YAMATO 			rt_off_t pos;
644d9e7fc5SMasatake YAMATO 				/* name of tag last searched for */
654d9e7fc5SMasatake YAMATO 			char *name;
664d9e7fc5SMasatake YAMATO 				/* length of name for partial matches */
674d9e7fc5SMasatake YAMATO 			size_t nameLength;
684d9e7fc5SMasatake YAMATO 				/* performing partial match */
694d9e7fc5SMasatake YAMATO 			short partial;
704d9e7fc5SMasatake YAMATO 				/* ignoring case */
714d9e7fc5SMasatake YAMATO 			short ignorecase;
724d9e7fc5SMasatake YAMATO 	} search;
734d9e7fc5SMasatake YAMATO 		/* miscellaneous extension fields */
744d9e7fc5SMasatake YAMATO 	struct {
754d9e7fc5SMasatake YAMATO 				/* number of entries in `list' */
764d9e7fc5SMasatake YAMATO 			unsigned short max;
774d9e7fc5SMasatake YAMATO 				/* list of key value pairs */
784d9e7fc5SMasatake YAMATO 			tagExtensionField *list;
794d9e7fc5SMasatake YAMATO 	} fields;
804d9e7fc5SMasatake YAMATO 		/* buffers to be freed at close */
814d9e7fc5SMasatake YAMATO 	struct {
824d9e7fc5SMasatake YAMATO 			/* name of program author */
834d9e7fc5SMasatake YAMATO 		char *author;
844d9e7fc5SMasatake YAMATO 			/* name of program */
854d9e7fc5SMasatake YAMATO 		char *name;
864d9e7fc5SMasatake YAMATO 			/* URL of distribution */
874d9e7fc5SMasatake YAMATO 		char *url;
884d9e7fc5SMasatake YAMATO 			/* program version */
894d9e7fc5SMasatake YAMATO 		char *version;
904d9e7fc5SMasatake YAMATO 	} program;
91a41b1651SMasatake YAMATO 		/* 0 (initial state set by calloc), errno value,
92a41b1651SMasatake YAMATO 		 * or tagErrno typed value */
93a41b1651SMasatake YAMATO 	int err;
944d9e7fc5SMasatake YAMATO };
954d9e7fc5SMasatake YAMATO 
964d9e7fc5SMasatake YAMATO /*
974d9e7fc5SMasatake YAMATO *   DATA DEFINITIONS
984d9e7fc5SMasatake YAMATO */
994d9e7fc5SMasatake YAMATO static const char *const EmptyString = "";
1004d9e7fc5SMasatake YAMATO static const char *const PseudoTagPrefix = "!_";
101ee45a800SMasatake YAMATO static const size_t PseudoTagPrefixLength = 2;
1024d9e7fc5SMasatake YAMATO 
1034d9e7fc5SMasatake YAMATO /*
1044d9e7fc5SMasatake YAMATO *   FUNCTION DEFINITIONS
1054d9e7fc5SMasatake YAMATO */
1064d9e7fc5SMasatake YAMATO 
readtags_ftell(FILE * fp)107*d5b81c4aSMasatake YAMATO static rt_off_t readtags_ftell(FILE *fp)
108*d5b81c4aSMasatake YAMATO {
109*d5b81c4aSMasatake YAMATO 	rt_off_t pos;
110*d5b81c4aSMasatake YAMATO 
111*d5b81c4aSMasatake YAMATO #ifdef _WIN32
112*d5b81c4aSMasatake YAMATO 	pos = _ftelli64(fp);
113*d5b81c4aSMasatake YAMATO #else
114*d5b81c4aSMasatake YAMATO 	pos = ftell(fp);
115*d5b81c4aSMasatake YAMATO #endif
116*d5b81c4aSMasatake YAMATO 
117*d5b81c4aSMasatake YAMATO 	return pos;
118*d5b81c4aSMasatake YAMATO }
119*d5b81c4aSMasatake YAMATO 
readtags_fseek(FILE * fp,rt_off_t pos,int whence)120*d5b81c4aSMasatake YAMATO static int readtags_fseek(FILE *fp, rt_off_t pos, int whence)
121*d5b81c4aSMasatake YAMATO {
122*d5b81c4aSMasatake YAMATO 	int ret;
123*d5b81c4aSMasatake YAMATO 
124*d5b81c4aSMasatake YAMATO #ifdef _WIN32
125*d5b81c4aSMasatake YAMATO 	ret = _fseeki64(fp, pos, whence);
126*d5b81c4aSMasatake YAMATO #else
127*d5b81c4aSMasatake YAMATO 	ret = fseek(fp, pos, whence);
128*d5b81c4aSMasatake YAMATO #endif
129*d5b81c4aSMasatake YAMATO 
130*d5b81c4aSMasatake YAMATO 	return ret;
131*d5b81c4aSMasatake YAMATO }
132*d5b81c4aSMasatake YAMATO 
1334d9e7fc5SMasatake YAMATO /* Converts a hexadecimal digit to its value */
xdigitValue(char digit)1344d9e7fc5SMasatake YAMATO static int xdigitValue (char digit)
1354d9e7fc5SMasatake YAMATO {
1364d9e7fc5SMasatake YAMATO 	if (digit >= '0' && digit <= '9')
1374d9e7fc5SMasatake YAMATO 		return digit - '0';
1384d9e7fc5SMasatake YAMATO 	else if (digit >= 'a' && digit <= 'f')
1394d9e7fc5SMasatake YAMATO 		return 10 + digit - 'a';
1404d9e7fc5SMasatake YAMATO 	else if (digit >= 'A' && digit <= 'F')
1414d9e7fc5SMasatake YAMATO 		return 10 + digit - 'A';
1424d9e7fc5SMasatake YAMATO 	else
1434d9e7fc5SMasatake YAMATO 		return 0;
1444d9e7fc5SMasatake YAMATO }
1454d9e7fc5SMasatake YAMATO 
1464d9e7fc5SMasatake YAMATO /*
1474d9e7fc5SMasatake YAMATO  * Reads the first character from the string, possibly un-escaping it, and
1484d9e7fc5SMasatake YAMATO  * advances *s to the start of the next character.
1494d9e7fc5SMasatake YAMATO  */
readTagCharacter(const char ** s)1504d9e7fc5SMasatake YAMATO static int readTagCharacter (const char **s)
1514d9e7fc5SMasatake YAMATO {
152e34ebe52SMasatake YAMATO 	int c = **(const unsigned char **)s;
1534d9e7fc5SMasatake YAMATO 
1544d9e7fc5SMasatake YAMATO 	(*s)++;
1554d9e7fc5SMasatake YAMATO 
1564d9e7fc5SMasatake YAMATO 	if (c == '\\')
1574d9e7fc5SMasatake YAMATO 	{
1584d9e7fc5SMasatake YAMATO 		switch (**s)
1594d9e7fc5SMasatake YAMATO 		{
1604d9e7fc5SMasatake YAMATO 			case 't': c = '\t'; (*s)++; break;
1614d9e7fc5SMasatake YAMATO 			case 'r': c = '\r'; (*s)++; break;
1624d9e7fc5SMasatake YAMATO 			case 'n': c = '\n'; (*s)++; break;
1634d9e7fc5SMasatake YAMATO 			case '\\': c = '\\'; (*s)++; break;
1644d9e7fc5SMasatake YAMATO 			/* Universal-CTags extensions */
1654d9e7fc5SMasatake YAMATO 			case 'a': c = '\a'; (*s)++; break;
1664d9e7fc5SMasatake YAMATO 			case 'b': c = '\b'; (*s)++; break;
1674d9e7fc5SMasatake YAMATO 			case 'v': c = '\v'; (*s)++; break;
1684d9e7fc5SMasatake YAMATO 			case 'f': c = '\f'; (*s)++; break;
1694d9e7fc5SMasatake YAMATO 			case 'x':
1704d9e7fc5SMasatake YAMATO 				if (isxdigit ((*s)[1]) && isxdigit ((*s)[2]))
1714d9e7fc5SMasatake YAMATO 				{
1724d9e7fc5SMasatake YAMATO 					int val = (xdigitValue ((*s)[1]) << 4) | xdigitValue ((*s)[2]);
1734d9e7fc5SMasatake YAMATO 					if (val < 0x80)
1744d9e7fc5SMasatake YAMATO 					{
1754d9e7fc5SMasatake YAMATO 						(*s) += 3;
1764d9e7fc5SMasatake YAMATO 						c = val;
1774d9e7fc5SMasatake YAMATO 					}
1784d9e7fc5SMasatake YAMATO 				}
1794d9e7fc5SMasatake YAMATO 				break;
1804d9e7fc5SMasatake YAMATO 		}
1814d9e7fc5SMasatake YAMATO 	}
1824d9e7fc5SMasatake YAMATO 
1834d9e7fc5SMasatake YAMATO 	return c;
1844d9e7fc5SMasatake YAMATO }
1854d9e7fc5SMasatake YAMATO 
1864d9e7fc5SMasatake YAMATO /*
1874d9e7fc5SMasatake YAMATO  * Compare two strings, ignoring case.
1884d9e7fc5SMasatake YAMATO  * Return 0 for match, < 0 for smaller, > 0 for bigger
1894d9e7fc5SMasatake YAMATO  * Make sure case is folded to uppercase in comparison (like for 'sort -f')
1904d9e7fc5SMasatake YAMATO  * This makes a difference when one of the chars lies between upper and lower
1914d9e7fc5SMasatake YAMATO  * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
1924d9e7fc5SMasatake YAMATO  */
taguppercmp(const char * s1,const char * s2)1934d9e7fc5SMasatake YAMATO static int taguppercmp (const char *s1, const char *s2)
1944d9e7fc5SMasatake YAMATO {
1954d9e7fc5SMasatake YAMATO 	int result;
1964d9e7fc5SMasatake YAMATO 	int c1, c2;
1974d9e7fc5SMasatake YAMATO 	do
1984d9e7fc5SMasatake YAMATO 	{
199e34ebe52SMasatake YAMATO 		c1 = (unsigned char)*s1++;
2004d9e7fc5SMasatake YAMATO 		c2 = readTagCharacter (&s2);
2014d9e7fc5SMasatake YAMATO 
2024d9e7fc5SMasatake YAMATO 		result = toupper (c1) - toupper (c2);
2034d9e7fc5SMasatake YAMATO 	} while (result == 0  &&  c1 != '\0'  &&  c2 != '\0');
2044d9e7fc5SMasatake YAMATO 	return result;
2054d9e7fc5SMasatake YAMATO }
2064d9e7fc5SMasatake YAMATO 
tagnuppercmp(const char * s1,const char * s2,size_t n)2074d9e7fc5SMasatake YAMATO static int tagnuppercmp (const char *s1, const char *s2, size_t n)
2084d9e7fc5SMasatake YAMATO {
2094d9e7fc5SMasatake YAMATO 	int result;
2104d9e7fc5SMasatake YAMATO 	int c1, c2;
2114d9e7fc5SMasatake YAMATO 	do
2124d9e7fc5SMasatake YAMATO 	{
213e34ebe52SMasatake YAMATO 		c1 = (unsigned char)*s1++;
2144d9e7fc5SMasatake YAMATO 		c2 = readTagCharacter (&s2);
2154d9e7fc5SMasatake YAMATO 
2164d9e7fc5SMasatake YAMATO 		result = toupper (c1) - toupper (c2);
2174d9e7fc5SMasatake YAMATO 	} while (result == 0  &&  --n > 0  &&  c1 != '\0'  &&  c2 != '\0');
2184d9e7fc5SMasatake YAMATO 	return result;
2194d9e7fc5SMasatake YAMATO }
2204d9e7fc5SMasatake YAMATO 
tagcmp(const char * s1,const char * s2)2214d9e7fc5SMasatake YAMATO static int tagcmp (const char *s1, const char *s2)
2224d9e7fc5SMasatake YAMATO {
2234d9e7fc5SMasatake YAMATO 	int result;
2244d9e7fc5SMasatake YAMATO 	int c1, c2;
2254d9e7fc5SMasatake YAMATO 	do
2264d9e7fc5SMasatake YAMATO 	{
227e34ebe52SMasatake YAMATO 		c1 = (unsigned char)*s1++;
2284d9e7fc5SMasatake YAMATO 		c2 = readTagCharacter (&s2);
2294d9e7fc5SMasatake YAMATO 
2304d9e7fc5SMasatake YAMATO 		result = c1 - c2;
2314d9e7fc5SMasatake YAMATO 	} while (result == 0  &&  c1 != '\0'  &&  c2 != '\0');
2324d9e7fc5SMasatake YAMATO 	return result;
2334d9e7fc5SMasatake YAMATO }
2344d9e7fc5SMasatake YAMATO 
tagncmp(const char * s1,const char * s2,size_t n)2354d9e7fc5SMasatake YAMATO static int tagncmp (const char *s1, const char *s2, size_t n)
2364d9e7fc5SMasatake YAMATO {
2374d9e7fc5SMasatake YAMATO 	int result;
2384d9e7fc5SMasatake YAMATO 	int c1, c2;
2394d9e7fc5SMasatake YAMATO 	do
2404d9e7fc5SMasatake YAMATO 	{
2414d9e7fc5SMasatake YAMATO 		c1 = *s1++;
2424d9e7fc5SMasatake YAMATO 		c2 = readTagCharacter (&s2);
2434d9e7fc5SMasatake YAMATO 
2444d9e7fc5SMasatake YAMATO 		result = c1 - c2;
2454d9e7fc5SMasatake YAMATO 	} while (result == 0  &&  --n > 0  &&  c1 != '\0'  &&  c2 != '\0');
2464d9e7fc5SMasatake YAMATO 	return result;
2474d9e7fc5SMasatake YAMATO }
2484d9e7fc5SMasatake YAMATO 
growString(vstring * s)249a41b1651SMasatake YAMATO static tagResult growString (vstring *s)
2504d9e7fc5SMasatake YAMATO {
251a41b1651SMasatake YAMATO 	tagResult result = TagFailure;
2524d9e7fc5SMasatake YAMATO 	size_t newLength;
2534d9e7fc5SMasatake YAMATO 	char *newLine;
2544d9e7fc5SMasatake YAMATO 	if (s->size == 0)
2554d9e7fc5SMasatake YAMATO 	{
2564d9e7fc5SMasatake YAMATO 		newLength = 128;
2574d9e7fc5SMasatake YAMATO 		newLine = (char*) malloc (newLength);
2584d9e7fc5SMasatake YAMATO 		if (newLine)
2594d9e7fc5SMasatake YAMATO 			*newLine = '\0';
2604d9e7fc5SMasatake YAMATO 	}
2614d9e7fc5SMasatake YAMATO 	else
2624d9e7fc5SMasatake YAMATO 	{
2634d9e7fc5SMasatake YAMATO 		newLength = 2 * s->size;
2644d9e7fc5SMasatake YAMATO 		newLine = (char*) realloc (s->buffer, newLength);
2654d9e7fc5SMasatake YAMATO 	}
2664d9e7fc5SMasatake YAMATO 	if (newLine == NULL)
2674d9e7fc5SMasatake YAMATO 		perror ("string too large");
2684d9e7fc5SMasatake YAMATO 	else
2694d9e7fc5SMasatake YAMATO 	{
2704d9e7fc5SMasatake YAMATO 		s->buffer = newLine;
2714d9e7fc5SMasatake YAMATO 		s->size = newLength;
272a41b1651SMasatake YAMATO 		result = TagSuccess;
2734d9e7fc5SMasatake YAMATO 	}
2744d9e7fc5SMasatake YAMATO 	return result;
2754d9e7fc5SMasatake YAMATO }
2764d9e7fc5SMasatake YAMATO 
2774d9e7fc5SMasatake YAMATO /* Copy name of tag out of tag line */
copyName(tagFile * const file)278a41b1651SMasatake YAMATO static tagResult copyName (tagFile *const file)
2794d9e7fc5SMasatake YAMATO {
2804d9e7fc5SMasatake YAMATO 	size_t length;
2814d9e7fc5SMasatake YAMATO 	const char *end = strchr (file->line.buffer, '\t');
2824d9e7fc5SMasatake YAMATO 	if (end == NULL)
2834d9e7fc5SMasatake YAMATO 	{
2844d9e7fc5SMasatake YAMATO 		end = strchr (file->line.buffer, '\n');
2854d9e7fc5SMasatake YAMATO 		if (end == NULL)
2864d9e7fc5SMasatake YAMATO 			end = strchr (file->line.buffer, '\r');
2874d9e7fc5SMasatake YAMATO 	}
2884d9e7fc5SMasatake YAMATO 	if (end != NULL)
2894d9e7fc5SMasatake YAMATO 		length = end - file->line.buffer;
2904d9e7fc5SMasatake YAMATO 	else
2914d9e7fc5SMasatake YAMATO 		length = strlen (file->line.buffer);
2924d9e7fc5SMasatake YAMATO 	while (length >= file->name.size)
293a41b1651SMasatake YAMATO 	{
294a41b1651SMasatake YAMATO 		if (growString (&file->name) != TagSuccess)
295a41b1651SMasatake YAMATO 			return TagFailure;
296a41b1651SMasatake YAMATO 	}
2974d9e7fc5SMasatake YAMATO 	strncpy (file->name.buffer, file->line.buffer, length);
2984d9e7fc5SMasatake YAMATO 	file->name.buffer [length] = '\0';
299a41b1651SMasatake YAMATO 	return TagSuccess;
3004d9e7fc5SMasatake YAMATO }
3014d9e7fc5SMasatake YAMATO 
302a41b1651SMasatake YAMATO /* Return 1 on success.
303a41b1651SMasatake YAMATO  * Return 0 on failure or EOF.
304a41b1651SMasatake YAMATO  * errno is set to *err unless EOF.
305a41b1651SMasatake YAMATO  */
readTagLineRaw(tagFile * const file,int * err)3066a50d4a4SMasatake YAMATO static int readTagLineRaw (tagFile *const file, int *err)
3074d9e7fc5SMasatake YAMATO {
3084d9e7fc5SMasatake YAMATO 	int result = 1;
3094d9e7fc5SMasatake YAMATO 	int reReadLine;
3104d9e7fc5SMasatake YAMATO 
3114d9e7fc5SMasatake YAMATO 	/*  If reading the line places any character other than a null or a
3124d9e7fc5SMasatake YAMATO 	 *  newline at the last character position in the buffer (one less than
3134d9e7fc5SMasatake YAMATO 	 *  the buffer size), then we must resize the buffer and reattempt to read
3144d9e7fc5SMasatake YAMATO 	 *  the line.
3154d9e7fc5SMasatake YAMATO 	 */
3164d9e7fc5SMasatake YAMATO 	do
3174d9e7fc5SMasatake YAMATO 	{
3184d9e7fc5SMasatake YAMATO 		char *const pLastChar = file->line.buffer + file->line.size - 2;
3194d9e7fc5SMasatake YAMATO 		char *line;
3204d9e7fc5SMasatake YAMATO 
321*d5b81c4aSMasatake YAMATO 		file->pos = readtags_ftell (file->fp);
322a41b1651SMasatake YAMATO 		if (file->pos < 0)
323a41b1651SMasatake YAMATO 		{
324a41b1651SMasatake YAMATO 			*err = errno;
325a41b1651SMasatake YAMATO 			result = 0;
326a41b1651SMasatake YAMATO 			break;
327a41b1651SMasatake YAMATO 		}
3284d9e7fc5SMasatake YAMATO 		reReadLine = 0;
3294d9e7fc5SMasatake YAMATO 		*pLastChar = '\0';
3304d9e7fc5SMasatake YAMATO 		line = fgets (file->line.buffer, (int) file->line.size, file->fp);
3314d9e7fc5SMasatake YAMATO 		if (line == NULL)
3324d9e7fc5SMasatake YAMATO 		{
3334d9e7fc5SMasatake YAMATO 			/* read error */
3346a50d4a4SMasatake YAMATO 			*err = 0;
3354d9e7fc5SMasatake YAMATO 			if (! feof (file->fp))
3366a50d4a4SMasatake YAMATO 				*err = errno;
3374d9e7fc5SMasatake YAMATO 			result = 0;
3384d9e7fc5SMasatake YAMATO 		}
3394d9e7fc5SMasatake YAMATO 		else if (*pLastChar != '\0'  &&
3404d9e7fc5SMasatake YAMATO 					*pLastChar != '\n'  &&  *pLastChar != '\r')
3414d9e7fc5SMasatake YAMATO 		{
3424d9e7fc5SMasatake YAMATO 			/*  buffer overflow */
343a41b1651SMasatake YAMATO 			if (growString (&file->line) != TagSuccess)
344a41b1651SMasatake YAMATO 			{
345a41b1651SMasatake YAMATO 				*err = ENOMEM;
346a41b1651SMasatake YAMATO 				result = 0;
347a41b1651SMasatake YAMATO 			}
348*d5b81c4aSMasatake YAMATO 
349*d5b81c4aSMasatake YAMATO 			if (readtags_fseek (file->fp, file->pos, SEEK_SET) < 0)
350a41b1651SMasatake YAMATO 			{
351a41b1651SMasatake YAMATO 				*err = errno;
352a41b1651SMasatake YAMATO 				result = 0;
353a41b1651SMasatake YAMATO 			}
3544d9e7fc5SMasatake YAMATO 			reReadLine = 1;
3554d9e7fc5SMasatake YAMATO 		}
3564d9e7fc5SMasatake YAMATO 		else
3574d9e7fc5SMasatake YAMATO 		{
3584d9e7fc5SMasatake YAMATO 			size_t i = strlen (file->line.buffer);
3594d9e7fc5SMasatake YAMATO 			while (i > 0  &&
3604d9e7fc5SMasatake YAMATO 				   (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r'))
3614d9e7fc5SMasatake YAMATO 			{
3624d9e7fc5SMasatake YAMATO 				file->line.buffer [i - 1] = '\0';
3634d9e7fc5SMasatake YAMATO 				--i;
3644d9e7fc5SMasatake YAMATO 			}
3654d9e7fc5SMasatake YAMATO 		}
3664d9e7fc5SMasatake YAMATO 	} while (reReadLine  &&  result);
3674d9e7fc5SMasatake YAMATO 	if (result)
368a41b1651SMasatake YAMATO 	{
369a41b1651SMasatake YAMATO 		if (copyName (file) != TagSuccess)
370a41b1651SMasatake YAMATO 		{
371a41b1651SMasatake YAMATO 			*err = ENOMEM;
372a41b1651SMasatake YAMATO 			result = 0;
373a41b1651SMasatake YAMATO 		}
374a41b1651SMasatake YAMATO 	}
3754d9e7fc5SMasatake YAMATO 	return result;
3764d9e7fc5SMasatake YAMATO }
3774d9e7fc5SMasatake YAMATO 
378a41b1651SMasatake YAMATO /* Return 1 on success.
379a41b1651SMasatake YAMATO  * Return 0 on failure or EOF.
380a41b1651SMasatake YAMATO  * errno is set to *err unless EOF.
381a41b1651SMasatake YAMATO  */
readTagLine(tagFile * const file,int * err)382a41b1651SMasatake YAMATO static int readTagLine (tagFile *const file, int *err)
3834d9e7fc5SMasatake YAMATO {
3844d9e7fc5SMasatake YAMATO 	int result;
3854d9e7fc5SMasatake YAMATO 	do
3864d9e7fc5SMasatake YAMATO 	{
3876a50d4a4SMasatake YAMATO 		result = readTagLineRaw (file, err);
3884d9e7fc5SMasatake YAMATO 	} while (result && *file->name.buffer == '\0');
3894d9e7fc5SMasatake YAMATO 	return result;
3904d9e7fc5SMasatake YAMATO }
3914d9e7fc5SMasatake YAMATO 
growFields(tagFile * const file)3924d9e7fc5SMasatake YAMATO static tagResult growFields (tagFile *const file)
3934d9e7fc5SMasatake YAMATO {
3944d9e7fc5SMasatake YAMATO 	tagResult result = TagFailure;
3954d9e7fc5SMasatake YAMATO 	unsigned short newCount = (unsigned short) 2 * file->fields.max;
3964d9e7fc5SMasatake YAMATO 	tagExtensionField *newFields = (tagExtensionField*)
3974d9e7fc5SMasatake YAMATO 			realloc (file->fields.list, newCount * sizeof (tagExtensionField));
3984d9e7fc5SMasatake YAMATO 	if (newFields == NULL)
3994d9e7fc5SMasatake YAMATO 		perror ("too many extension fields");
4004d9e7fc5SMasatake YAMATO 	else
4014d9e7fc5SMasatake YAMATO 	{
4024d9e7fc5SMasatake YAMATO 		file->fields.list = newFields;
4034d9e7fc5SMasatake YAMATO 		file->fields.max = newCount;
4044d9e7fc5SMasatake YAMATO 		result = TagSuccess;
4054d9e7fc5SMasatake YAMATO 	}
4064d9e7fc5SMasatake YAMATO 	return result;
4074d9e7fc5SMasatake YAMATO }
4084d9e7fc5SMasatake YAMATO 
parseExtensionFields(tagFile * const file,tagEntry * const entry,char * const string,int * err)409a41b1651SMasatake YAMATO static tagResult parseExtensionFields (tagFile *const file, tagEntry *const entry,
410a41b1651SMasatake YAMATO 									   char *const string, int *err)
4114d9e7fc5SMasatake YAMATO {
4124d9e7fc5SMasatake YAMATO 	char *p = string;
413aeae4e55SMasatake YAMATO 	char *tail = string + (string? strlen(string):0);
414aeae4e55SMasatake YAMATO 	size_t q_len;
415aeae4e55SMasatake YAMATO 
4164d9e7fc5SMasatake YAMATO 	while (p != NULL  &&  *p != '\0')
4174d9e7fc5SMasatake YAMATO 	{
4184d9e7fc5SMasatake YAMATO 		while (*p == TAB)
4194d9e7fc5SMasatake YAMATO 			*p++ = '\0';
4204d9e7fc5SMasatake YAMATO 		if (*p != '\0')
4214d9e7fc5SMasatake YAMATO 		{
4224d9e7fc5SMasatake YAMATO 			char *colon;
4234d9e7fc5SMasatake YAMATO 			char *field = p;
4244d9e7fc5SMasatake YAMATO 			p = strchr (p, TAB);
4254d9e7fc5SMasatake YAMATO 			if (p != NULL)
4264d9e7fc5SMasatake YAMATO 				*p++ = '\0';
4274d9e7fc5SMasatake YAMATO 			colon = strchr (field, ':');
4284d9e7fc5SMasatake YAMATO 			if (colon == NULL)
4294d9e7fc5SMasatake YAMATO 				entry->kind = field;
4304d9e7fc5SMasatake YAMATO 			else
4314d9e7fc5SMasatake YAMATO 			{
4324d9e7fc5SMasatake YAMATO 				const char *key = field;
433aeae4e55SMasatake YAMATO 				char *q = colon + 1;
434aeae4e55SMasatake YAMATO 				const char *value = q;
4354d9e7fc5SMasatake YAMATO 				const int key_len = colon - key;
4364d9e7fc5SMasatake YAMATO 				*colon = '\0';
437aeae4e55SMasatake YAMATO 
438aeae4e55SMasatake YAMATO 				q_len = tail - q;
439aeae4e55SMasatake YAMATO 
440aeae4e55SMasatake YAMATO 				/* Unescaping */
441aeae4e55SMasatake YAMATO 				while (*q != '\0')
442aeae4e55SMasatake YAMATO 				{
443aeae4e55SMasatake YAMATO 					const char *next = q;
444aeae4e55SMasatake YAMATO 					int ch = readTagCharacter (&next);
445aeae4e55SMasatake YAMATO 					size_t skip = next - q;
446aeae4e55SMasatake YAMATO 
447aeae4e55SMasatake YAMATO 					*q = (char) ch;
448aeae4e55SMasatake YAMATO 					q++;
449aeae4e55SMasatake YAMATO 					q_len -= skip;
450aeae4e55SMasatake YAMATO 					if (skip > 1)
451aeae4e55SMasatake YAMATO 					{
452aeae4e55SMasatake YAMATO 						/* + 1 is for moving the area including the last '\0'. */
453aeae4e55SMasatake YAMATO 						memmove (q, next, q_len + 1);
454aeae4e55SMasatake YAMATO 						if (p)
455aeae4e55SMasatake YAMATO 							p -= skip - 1;
456aeae4e55SMasatake YAMATO 						if (tail != string)
457aeae4e55SMasatake YAMATO 							tail -= skip - 1;
458aeae4e55SMasatake YAMATO 					}
459aeae4e55SMasatake YAMATO 				}
460aeae4e55SMasatake YAMATO 
4614d9e7fc5SMasatake YAMATO 				if (key_len == 4)
4624d9e7fc5SMasatake YAMATO 				{
4634d9e7fc5SMasatake YAMATO 					if (memcmp (key, "kind", 4) == 0)
4644d9e7fc5SMasatake YAMATO 						entry->kind = value;
4654d9e7fc5SMasatake YAMATO 					else if (memcmp (key, "file", 4) == 0)
4664d9e7fc5SMasatake YAMATO 						entry->fileScope = 1;
4674d9e7fc5SMasatake YAMATO 					else if (memcmp (key, "line", 4) == 0)
468a41b1651SMasatake YAMATO 					{
469a41b1651SMasatake YAMATO 						char *endptr = NULL;
470a41b1651SMasatake YAMATO 						long m = strtol (value, &endptr, 10);
471a41b1651SMasatake YAMATO 						if (*endptr != '\0' || m < 0)
472a41b1651SMasatake YAMATO 						{
473a41b1651SMasatake YAMATO 							*err = TagErrnoUnexpectedLineno;
474a41b1651SMasatake YAMATO 							return TagFailure;
475a41b1651SMasatake YAMATO 						}
476a41b1651SMasatake YAMATO 						entry->address.lineNumber = m;
477a41b1651SMasatake YAMATO 					}
4784d9e7fc5SMasatake YAMATO 					else
4794d9e7fc5SMasatake YAMATO 						goto normalField;
4804d9e7fc5SMasatake YAMATO 				}
4814d9e7fc5SMasatake YAMATO 				else
4824d9e7fc5SMasatake YAMATO 				{
4834d9e7fc5SMasatake YAMATO 				normalField:
4844d9e7fc5SMasatake YAMATO 					if (entry->fields.count == file->fields.max)
485a41b1651SMasatake YAMATO 					{
486a41b1651SMasatake YAMATO 						if (growFields (file) != TagSuccess)
487a41b1651SMasatake YAMATO 						{
488a41b1651SMasatake YAMATO 							*err = ENOMEM;
489a41b1651SMasatake YAMATO 							return TagFailure;
490a41b1651SMasatake YAMATO 						}
491a41b1651SMasatake YAMATO 					}
4924d9e7fc5SMasatake YAMATO 					file->fields.list [entry->fields.count].key = key;
4934d9e7fc5SMasatake YAMATO 					file->fields.list [entry->fields.count].value = value;
4944d9e7fc5SMasatake YAMATO 					++entry->fields.count;
4954d9e7fc5SMasatake YAMATO 				}
4964d9e7fc5SMasatake YAMATO 			}
4974d9e7fc5SMasatake YAMATO 		}
4984d9e7fc5SMasatake YAMATO 	}
499a41b1651SMasatake YAMATO 	return TagSuccess;
5004d9e7fc5SMasatake YAMATO }
5014d9e7fc5SMasatake YAMATO 
isOdd(unsigned int i)5024d9e7fc5SMasatake YAMATO static int isOdd (unsigned int i)
5034d9e7fc5SMasatake YAMATO {
5044d9e7fc5SMasatake YAMATO 	return  (i % 2);
5054d9e7fc5SMasatake YAMATO }
5064d9e7fc5SMasatake YAMATO 
countContinuousBackslashesBackward(const char * from,const char * till)5074d9e7fc5SMasatake YAMATO static unsigned int countContinuousBackslashesBackward(const char *from,
5084d9e7fc5SMasatake YAMATO 						     const char *till)
5094d9e7fc5SMasatake YAMATO {
5104d9e7fc5SMasatake YAMATO 	unsigned int counter = 0;
5114d9e7fc5SMasatake YAMATO 
5124d9e7fc5SMasatake YAMATO 	for (; from > till; from--)
5134d9e7fc5SMasatake YAMATO 	{
5144d9e7fc5SMasatake YAMATO 		if (*from == '\\')
5154d9e7fc5SMasatake YAMATO 			counter++;
5164d9e7fc5SMasatake YAMATO 		else
5174d9e7fc5SMasatake YAMATO 			break;
5184d9e7fc5SMasatake YAMATO 	}
5194d9e7fc5SMasatake YAMATO 	return counter;
5204d9e7fc5SMasatake YAMATO }
5214d9e7fc5SMasatake YAMATO 
parseTagLine(tagFile * file,tagEntry * const entry,int * err)522a41b1651SMasatake YAMATO static tagResult parseTagLine (tagFile *file, tagEntry *const entry, int *err)
5234d9e7fc5SMasatake YAMATO {
5244d9e7fc5SMasatake YAMATO 	int i;
5254d9e7fc5SMasatake YAMATO 	char *p = file->line.buffer;
5264d9e7fc5SMasatake YAMATO 	size_t p_len = strlen (p);
5274d9e7fc5SMasatake YAMATO 	char *tab = strchr (p, TAB);
5284d9e7fc5SMasatake YAMATO 
5294d9e7fc5SMasatake YAMATO 	memset(entry, 0, sizeof(*entry));
5304d9e7fc5SMasatake YAMATO 
5314d9e7fc5SMasatake YAMATO 	entry->name = p;
5324d9e7fc5SMasatake YAMATO 	if (tab != NULL)
5334d9e7fc5SMasatake YAMATO 	{
5344d9e7fc5SMasatake YAMATO 		*tab = '\0';
5354d9e7fc5SMasatake YAMATO 	}
5367bb34c76SMasatake YAMATO 
5377bb34c76SMasatake YAMATO 	/* When unescaping, the input string becomes shorter.
5387bb34c76SMasatake YAMATO 	 * e.g. \t occupies two bytes on the tag file.
5397bb34c76SMasatake YAMATO 	 * It is converted to 0x9 and occupies one byte.
5407bb34c76SMasatake YAMATO 	 * memmove called here for shortening the line
5417bb34c76SMasatake YAMATO 	 * buffer. */
5424d9e7fc5SMasatake YAMATO 	while (*p != '\0')
5434d9e7fc5SMasatake YAMATO 	{
5444d9e7fc5SMasatake YAMATO 		const char *next = p;
5454d9e7fc5SMasatake YAMATO 		int ch = readTagCharacter (&next);
5464d9e7fc5SMasatake YAMATO 		size_t skip = next - p;
5474d9e7fc5SMasatake YAMATO 
5484d9e7fc5SMasatake YAMATO 		*p = (char) ch;
5494d9e7fc5SMasatake YAMATO 		p++;
5504d9e7fc5SMasatake YAMATO 		p_len -= skip;
5514d9e7fc5SMasatake YAMATO 		if (skip > 1)
5524d9e7fc5SMasatake YAMATO 		{
5537bb34c76SMasatake YAMATO 			/* + 1 is for moving the area including the last '\0'. */
5547bb34c76SMasatake YAMATO 			memmove (p, next, p_len + 1);
555aeae4e55SMasatake YAMATO 			if (tab)
5564d9e7fc5SMasatake YAMATO 				tab -= skip - 1;
5574d9e7fc5SMasatake YAMATO 		}
5584d9e7fc5SMasatake YAMATO 	}
5594d9e7fc5SMasatake YAMATO 
5604d9e7fc5SMasatake YAMATO 	if (tab != NULL)
5614d9e7fc5SMasatake YAMATO 	{
5624d9e7fc5SMasatake YAMATO 		p = tab + 1;
5634d9e7fc5SMasatake YAMATO 		entry->file = p;
5644d9e7fc5SMasatake YAMATO 		tab = strchr (p, TAB);
5654d9e7fc5SMasatake YAMATO 		if (tab != NULL)
5664d9e7fc5SMasatake YAMATO 		{
5674d9e7fc5SMasatake YAMATO 			int fieldsPresent;
5684d9e7fc5SMasatake YAMATO 			int combinedPattern;
5694d9e7fc5SMasatake YAMATO 			*tab = '\0';
5704d9e7fc5SMasatake YAMATO 			p = tab + 1;
5714d9e7fc5SMasatake YAMATO 			if (*p == '/'  ||  *p == '?')
5724d9e7fc5SMasatake YAMATO 			{
5734d9e7fc5SMasatake YAMATO 				/* parse pattern */
5744d9e7fc5SMasatake YAMATO 				int delimiter = *(unsigned char*) p;
5754d9e7fc5SMasatake YAMATO 				entry->address.lineNumber = 0;
5764d9e7fc5SMasatake YAMATO 				entry->address.pattern = p;
5774d9e7fc5SMasatake YAMATO 				do
5784d9e7fc5SMasatake YAMATO 				{
5794d9e7fc5SMasatake YAMATO 					p = strchr (p + 1, delimiter);
5804d9e7fc5SMasatake YAMATO 				} while (p != NULL
5814d9e7fc5SMasatake YAMATO 					 &&  isOdd (countContinuousBackslashesBackward (p - 1,
5824d9e7fc5SMasatake YAMATO 											entry->address.pattern)));
5834d9e7fc5SMasatake YAMATO 
5844d9e7fc5SMasatake YAMATO 				if (p == NULL)
5854d9e7fc5SMasatake YAMATO 				{
586a41b1651SMasatake YAMATO 					/* TODO: invalid pattern */
5874d9e7fc5SMasatake YAMATO 				}
5884d9e7fc5SMasatake YAMATO 				else
5894d9e7fc5SMasatake YAMATO 					++p;
5904d9e7fc5SMasatake YAMATO 			}
5914d9e7fc5SMasatake YAMATO 			else if (isdigit ((int) *(unsigned char*) p))
5924d9e7fc5SMasatake YAMATO 			{
5934d9e7fc5SMasatake YAMATO 				/* parse line number */
5944d9e7fc5SMasatake YAMATO 				entry->address.pattern = p;
5954d9e7fc5SMasatake YAMATO 				entry->address.lineNumber = atol (p);
5964d9e7fc5SMasatake YAMATO 				while (isdigit ((int) *(unsigned char*) p))
5974d9e7fc5SMasatake YAMATO 					++p;
5984d9e7fc5SMasatake YAMATO 				if (p)
5994d9e7fc5SMasatake YAMATO 				{
6004d9e7fc5SMasatake YAMATO 					combinedPattern = (strncmp (p, ";/", 2) == 0) ||
6014d9e7fc5SMasatake YAMATO 											(strncmp (p, ";?", 2) == 0);
6024d9e7fc5SMasatake YAMATO 					if (combinedPattern)
6034d9e7fc5SMasatake YAMATO 					{
6044d9e7fc5SMasatake YAMATO 						++p;
6054d9e7fc5SMasatake YAMATO 						/* parse pattern */
6064d9e7fc5SMasatake YAMATO 						int delimiter = *(unsigned char*) p;
6074d9e7fc5SMasatake YAMATO 						do
6084d9e7fc5SMasatake YAMATO 						{
6094d9e7fc5SMasatake YAMATO 							p = strchr (p + 1, delimiter);
6104d9e7fc5SMasatake YAMATO 						} while (p != NULL
6114d9e7fc5SMasatake YAMATO 							 &&  isOdd (countContinuousBackslashesBackward (p - 1,
6124d9e7fc5SMasatake YAMATO 													entry->address.pattern)));
6134d9e7fc5SMasatake YAMATO 
6144d9e7fc5SMasatake YAMATO 						if (p == NULL)
6154d9e7fc5SMasatake YAMATO 						{
616a41b1651SMasatake YAMATO 							/* TODO: invalid pattern */
6174d9e7fc5SMasatake YAMATO 						}
6184d9e7fc5SMasatake YAMATO 						else
6194d9e7fc5SMasatake YAMATO 							++p;
6204d9e7fc5SMasatake YAMATO 					}
6214d9e7fc5SMasatake YAMATO 				}
6224d9e7fc5SMasatake YAMATO 			}
6234d9e7fc5SMasatake YAMATO 			else
6244d9e7fc5SMasatake YAMATO 			{
625a41b1651SMasatake YAMATO 				/* TODO: invalid pattern */
6264d9e7fc5SMasatake YAMATO 			}
6274d9e7fc5SMasatake YAMATO 
6284d9e7fc5SMasatake YAMATO 			if (p)
6294d9e7fc5SMasatake YAMATO 			{
6304d9e7fc5SMasatake YAMATO 				fieldsPresent = (strncmp (p, ";\"", 2) == 0);
6314d9e7fc5SMasatake YAMATO 				*p = '\0';
6324d9e7fc5SMasatake YAMATO 				if (fieldsPresent)
633a41b1651SMasatake YAMATO 				{
634a41b1651SMasatake YAMATO 					if (parseExtensionFields (file, entry, p + 2, err) != TagSuccess)
635a41b1651SMasatake YAMATO 						return TagFailure;
636a41b1651SMasatake YAMATO 				}
6374d9e7fc5SMasatake YAMATO 			}
6384d9e7fc5SMasatake YAMATO 		}
6394d9e7fc5SMasatake YAMATO 	}
6404d9e7fc5SMasatake YAMATO 	if (entry->fields.count > 0)
6414d9e7fc5SMasatake YAMATO 		entry->fields.list = file->fields.list;
6424d9e7fc5SMasatake YAMATO 	for (i = entry->fields.count  ;  i < file->fields.max  ;  ++i)
6434d9e7fc5SMasatake YAMATO 	{
6444d9e7fc5SMasatake YAMATO 		file->fields.list [i].key = NULL;
6454d9e7fc5SMasatake YAMATO 		file->fields.list [i].value = NULL;
6464d9e7fc5SMasatake YAMATO 	}
647a41b1651SMasatake YAMATO 	return TagSuccess;
6484d9e7fc5SMasatake YAMATO }
6494d9e7fc5SMasatake YAMATO 
duplicate(const char * str)6504d9e7fc5SMasatake YAMATO static char *duplicate (const char *str)
6514d9e7fc5SMasatake YAMATO {
6524d9e7fc5SMasatake YAMATO 	char *result = NULL;
6534d9e7fc5SMasatake YAMATO 	if (str != NULL)
6544d9e7fc5SMasatake YAMATO 	{
6554d9e7fc5SMasatake YAMATO 		result = strdup (str);
6564d9e7fc5SMasatake YAMATO 		if (result == NULL)
6574d9e7fc5SMasatake YAMATO 			perror (NULL);
6584d9e7fc5SMasatake YAMATO 	}
6594d9e7fc5SMasatake YAMATO 	return result;
6604d9e7fc5SMasatake YAMATO }
6614d9e7fc5SMasatake YAMATO 
isPseudoTagLine(const char * buffer)662ee45a800SMasatake YAMATO static int isPseudoTagLine (const char *buffer)
663ee45a800SMasatake YAMATO {
664ee45a800SMasatake YAMATO 	return (strncmp (buffer, PseudoTagPrefix, PseudoTagPrefixLength) == 0);
665ee45a800SMasatake YAMATO }
666ee45a800SMasatake YAMATO 
readPseudoTags(tagFile * const file,tagFileInfo * const info)667a41b1651SMasatake YAMATO static tagResult readPseudoTags (tagFile *const file, tagFileInfo *const info)
6684d9e7fc5SMasatake YAMATO {
6694d9e7fc5SMasatake YAMATO 	fpos_t startOfLine;
6706a50d4a4SMasatake YAMATO 	int err = 0;
671a41b1651SMasatake YAMATO 	tagResult result = TagSuccess;
6724d9e7fc5SMasatake YAMATO 	const size_t prefixLength = strlen (PseudoTagPrefix);
673a41b1651SMasatake YAMATO 
6744d9e7fc5SMasatake YAMATO 	info->file.format     = 1;
6754d9e7fc5SMasatake YAMATO 	info->file.sort       = TAG_UNSORTED;
6764d9e7fc5SMasatake YAMATO 	info->program.author  = NULL;
6774d9e7fc5SMasatake YAMATO 	info->program.name    = NULL;
6784d9e7fc5SMasatake YAMATO 	info->program.url     = NULL;
6794d9e7fc5SMasatake YAMATO 	info->program.version = NULL;
680a41b1651SMasatake YAMATO 
6814d9e7fc5SMasatake YAMATO 	while (1)
6824d9e7fc5SMasatake YAMATO 	{
683a41b1651SMasatake YAMATO 		if (fgetpos (file->fp, &startOfLine) < 0)
684a41b1651SMasatake YAMATO 		{
685a41b1651SMasatake YAMATO 			err = errno;
686a41b1651SMasatake YAMATO 			break;
687a41b1651SMasatake YAMATO 		}
688a41b1651SMasatake YAMATO 		if (! readTagLine (file, &err))
6894d9e7fc5SMasatake YAMATO 			break;
690ee45a800SMasatake YAMATO 		if (!isPseudoTagLine (file->line.buffer))
6914d9e7fc5SMasatake YAMATO 			break;
6924d9e7fc5SMasatake YAMATO 		else
6934d9e7fc5SMasatake YAMATO 		{
6944d9e7fc5SMasatake YAMATO 			tagEntry entry;
6954d9e7fc5SMasatake YAMATO 			const char *key, *value;
696a41b1651SMasatake YAMATO 			if (parseTagLine (file, &entry, &err) != TagSuccess)
697a41b1651SMasatake YAMATO 				break;
6984d9e7fc5SMasatake YAMATO 			key = entry.name + prefixLength;
6994d9e7fc5SMasatake YAMATO 			value = entry.file;
7004d9e7fc5SMasatake YAMATO 			if (strcmp (key, "TAG_FILE_SORTED") == 0)
7014d9e7fc5SMasatake YAMATO 			{
702a41b1651SMasatake YAMATO 				char *endptr = NULL;
703a41b1651SMasatake YAMATO 				long m = strtol (value, &endptr, 10);
704a41b1651SMasatake YAMATO 				if (*endptr != '\0' || m < 0 || m > 2)
705a41b1651SMasatake YAMATO 				{
706a41b1651SMasatake YAMATO 					err = TagErrnoUnexpectedSortedMethod;
707a41b1651SMasatake YAMATO 					break;
708a41b1651SMasatake YAMATO 				}
709a41b1651SMasatake YAMATO 				file->sortMethod = (tagSortType) m;
710a41b1651SMasatake YAMATO 			}
711a41b1651SMasatake YAMATO 			else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
712a41b1651SMasatake YAMATO 			{
713a41b1651SMasatake YAMATO 				char *endptr = NULL;
714a41b1651SMasatake YAMATO 				long m = strtol (value, &endptr, 10);
715a41b1651SMasatake YAMATO 				if (*endptr != '\0' || m < 1 || m > 2)
716a41b1651SMasatake YAMATO 				{
717a41b1651SMasatake YAMATO 					err = TagErrnoUnexpectedFormat;
718a41b1651SMasatake YAMATO 					break;
719a41b1651SMasatake YAMATO 				}
720a41b1651SMasatake YAMATO 				file->format = (short) m;
721a41b1651SMasatake YAMATO 			}
722a41b1651SMasatake YAMATO 			else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
723a41b1651SMasatake YAMATO 			{
724a41b1651SMasatake YAMATO 				file->program.author = duplicate (value);
725a41b1651SMasatake YAMATO 				if (value && file->program.author == NULL)
726a41b1651SMasatake YAMATO 				{
727a41b1651SMasatake YAMATO 					err = ENOMEM;
728a41b1651SMasatake YAMATO 					break;
729a41b1651SMasatake YAMATO 				}
730a41b1651SMasatake YAMATO 			}
731a41b1651SMasatake YAMATO 			else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
732a41b1651SMasatake YAMATO 			{
733a41b1651SMasatake YAMATO 				file->program.name = duplicate (value);
734a41b1651SMasatake YAMATO 				if (value && file->program.name == NULL)
735a41b1651SMasatake YAMATO 				{
736a41b1651SMasatake YAMATO 					err = ENOMEM;
737a41b1651SMasatake YAMATO 					break;
738a41b1651SMasatake YAMATO 				}
739a41b1651SMasatake YAMATO 			}
740a41b1651SMasatake YAMATO 			else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
741a41b1651SMasatake YAMATO 			{
742a41b1651SMasatake YAMATO 				file->program.url = duplicate (value);
743a41b1651SMasatake YAMATO 				if (value && file->program.url == NULL)
744a41b1651SMasatake YAMATO 				{
745a41b1651SMasatake YAMATO 					err = ENOMEM;
746a41b1651SMasatake YAMATO 					break;
747a41b1651SMasatake YAMATO 				}
748a41b1651SMasatake YAMATO 			}
749a41b1651SMasatake YAMATO 			else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
750a41b1651SMasatake YAMATO 			{
751a41b1651SMasatake YAMATO 				file->program.version = duplicate (value);
752a41b1651SMasatake YAMATO 				if (value && file->program.version == NULL)
753a41b1651SMasatake YAMATO 				{
754a41b1651SMasatake YAMATO 					err = ENOMEM;
755a41b1651SMasatake YAMATO 					break;
756a41b1651SMasatake YAMATO 				}
757a41b1651SMasatake YAMATO 			}
758a41b1651SMasatake YAMATO 
7594d9e7fc5SMasatake YAMATO 			info->file.format     = file->format;
7604d9e7fc5SMasatake YAMATO 			info->file.sort       = file->sortMethod;
7614d9e7fc5SMasatake YAMATO 			info->program.author  = file->program.author;
7624d9e7fc5SMasatake YAMATO 			info->program.name    = file->program.name;
7634d9e7fc5SMasatake YAMATO 			info->program.url     = file->program.url;
7644d9e7fc5SMasatake YAMATO 			info->program.version = file->program.version;
7654d9e7fc5SMasatake YAMATO 		}
7664d9e7fc5SMasatake YAMATO 	}
767a41b1651SMasatake YAMATO 	if (fsetpos (file->fp, &startOfLine) < 0)
768a41b1651SMasatake YAMATO 		err = errno;
769a41b1651SMasatake YAMATO 
770a41b1651SMasatake YAMATO 	info->status.error_number = err;
771a41b1651SMasatake YAMATO 	if (err)
772a41b1651SMasatake YAMATO 		result = TagFailure;
773a41b1651SMasatake YAMATO 	return result;
7744d9e7fc5SMasatake YAMATO }
7754d9e7fc5SMasatake YAMATO 
doesFilePointPseudoTag(tagFile * const file,void * unused)776ee45a800SMasatake YAMATO static int doesFilePointPseudoTag (tagFile *const file, void *unused)
777ee45a800SMasatake YAMATO {
778ee45a800SMasatake YAMATO 	return isPseudoTagLine (file->name.buffer);
779ee45a800SMasatake YAMATO }
780ee45a800SMasatake YAMATO 
gotoFirstLogicalTag(tagFile * const file)781a41b1651SMasatake YAMATO static tagResult gotoFirstLogicalTag (tagFile *const file)
7824d9e7fc5SMasatake YAMATO {
7834d9e7fc5SMasatake YAMATO 	fpos_t startOfLine;
784a41b1651SMasatake YAMATO 
785*d5b81c4aSMasatake YAMATO 	if (readtags_fseek(file->fp, 0, SEEK_SET) == -1)
786a41b1651SMasatake YAMATO 	{
787a41b1651SMasatake YAMATO 		file->err = errno;
788a41b1651SMasatake YAMATO 		return TagFailure;
789a41b1651SMasatake YAMATO 	}
790a41b1651SMasatake YAMATO 
7914d9e7fc5SMasatake YAMATO 	while (1)
7924d9e7fc5SMasatake YAMATO 	{
793a41b1651SMasatake YAMATO 		if (fgetpos (file->fp, &startOfLine) < 0)
794a41b1651SMasatake YAMATO 		{
795a41b1651SMasatake YAMATO 			file->err = errno;
796a41b1651SMasatake YAMATO 			return TagFailure;
797a41b1651SMasatake YAMATO 		}
798a41b1651SMasatake YAMATO 		if (! readTagLine (file, &file->err))
799a41b1651SMasatake YAMATO 		{
800a41b1651SMasatake YAMATO 			if (file->err)
801a41b1651SMasatake YAMATO 				return TagFailure;
8024d9e7fc5SMasatake YAMATO 			break;
803a41b1651SMasatake YAMATO 		}
804ee45a800SMasatake YAMATO 		if (!isPseudoTagLine (file->line.buffer))
8054d9e7fc5SMasatake YAMATO 			break;
8064d9e7fc5SMasatake YAMATO 	}
807a41b1651SMasatake YAMATO 	if (fsetpos (file->fp, &startOfLine) < 0)
808a41b1651SMasatake YAMATO 	{
809a41b1651SMasatake YAMATO 		file->err = errno;
810a41b1651SMasatake YAMATO 		return TagFailure;
811a41b1651SMasatake YAMATO 	}
812a41b1651SMasatake YAMATO 	return TagSuccess;
8134d9e7fc5SMasatake YAMATO }
8144d9e7fc5SMasatake YAMATO 
initialize(const char * const filePath,tagFileInfo * const info)8154d9e7fc5SMasatake YAMATO static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
8164d9e7fc5SMasatake YAMATO {
8174d9e7fc5SMasatake YAMATO 	tagFile *result = (tagFile*) calloc ((size_t) 1, sizeof (tagFile));
818a41b1651SMasatake YAMATO 
819a41b1651SMasatake YAMATO 	if (result == NULL)
8204d9e7fc5SMasatake YAMATO 	{
821a41b1651SMasatake YAMATO 		info->status.opened = 0;
822a41b1651SMasatake YAMATO 		info->status.error_number = ENOMEM;
823a41b1651SMasatake YAMATO 		return NULL;
824a41b1651SMasatake YAMATO 	}
825a41b1651SMasatake YAMATO 
826a41b1651SMasatake YAMATO 	if (growString (&result->line) != TagSuccess)
827ee45a800SMasatake YAMATO 		goto mem_error;
828a41b1651SMasatake YAMATO 	if (growString (&result->name) != TagSuccess)
829ee45a800SMasatake YAMATO 		goto mem_error;
8304d9e7fc5SMasatake YAMATO 	result->fields.max = 20;
8314d9e7fc5SMasatake YAMATO 	result->fields.list = (tagExtensionField*) calloc (
8324d9e7fc5SMasatake YAMATO 		result->fields.max, sizeof (tagExtensionField));
833ee45a800SMasatake YAMATO 	if (result->fields.list == NULL)
834ee45a800SMasatake YAMATO 		goto mem_error;
8354d9e7fc5SMasatake YAMATO 	result->fp = fopen (filePath, "rb");
8364d9e7fc5SMasatake YAMATO 	if (result->fp == NULL)
8374d9e7fc5SMasatake YAMATO 	{
8384d9e7fc5SMasatake YAMATO 		info->status.error_number = errno;
839ee45a800SMasatake YAMATO 		goto file_error;
8404d9e7fc5SMasatake YAMATO 	}
841*d5b81c4aSMasatake YAMATO 
842*d5b81c4aSMasatake YAMATO 	/* Record the size of the tags file to `size` field of result. */
843*d5b81c4aSMasatake YAMATO 	if (readtags_fseek (result->fp, 0, SEEK_END) == -1)
844ee45a800SMasatake YAMATO 	{
845ee45a800SMasatake YAMATO 		info->status.error_number = errno;
846ee45a800SMasatake YAMATO 		goto file_error;
847ee45a800SMasatake YAMATO 	}
848*d5b81c4aSMasatake YAMATO 	result->size = readtags_ftell (result->fp);
849ee45a800SMasatake YAMATO 	if (result->size == -1)
850ee45a800SMasatake YAMATO 	{
851*d5b81c4aSMasatake YAMATO 		/* fseek() retruns an int value.
852*d5b81c4aSMasatake YAMATO 		 * We observed following behavior on Windows;
853*d5b81c4aSMasatake YAMATO 		 * if sizeof(int) of the platform is too small for
854*d5b81c4aSMasatake YAMATO 		 * representing the size of the tags file, fseek()
855*d5b81c4aSMasatake YAMATO 		 * returns -1 and it doesn't set errno.
856*d5b81c4aSMasatake YAMATO 		 */
857ee45a800SMasatake YAMATO 		info->status.error_number = errno;
858*d5b81c4aSMasatake YAMATO 		if (info->status.error_number == 0)
859*d5b81c4aSMasatake YAMATO 			info->status.error_number = TagErrnoFileMaybeTooBig;
860*d5b81c4aSMasatake YAMATO 
861ee45a800SMasatake YAMATO 		goto file_error;
862ee45a800SMasatake YAMATO 	}
863*d5b81c4aSMasatake YAMATO 	if (readtags_fseek(result->fp, 0, SEEK_SET) == -1)
864a41b1651SMasatake YAMATO 	{
865a41b1651SMasatake YAMATO 		info->status.error_number = errno;
866a41b1651SMasatake YAMATO 		goto file_error;
867a41b1651SMasatake YAMATO 	}
8686a50d4a4SMasatake YAMATO 
869a41b1651SMasatake YAMATO 	if (readPseudoTags (result, info) == TagFailure)
8706a50d4a4SMasatake YAMATO 		goto file_error;
8716a50d4a4SMasatake YAMATO 
8724d9e7fc5SMasatake YAMATO 	info->status.opened = 1;
8734d9e7fc5SMasatake YAMATO 	result->initialized = 1;
874*d5b81c4aSMasatake YAMATO 
8754d9e7fc5SMasatake YAMATO 	return result;
876ee45a800SMasatake YAMATO  mem_error:
877a41b1651SMasatake YAMATO 	info->status.error_number = ENOMEM;
878ee45a800SMasatake YAMATO  file_error:
879ee45a800SMasatake YAMATO 	free (result->line.buffer);
880ee45a800SMasatake YAMATO 	free (result->name.buffer);
881ee45a800SMasatake YAMATO 	free (result->fields.list);
8826a50d4a4SMasatake YAMATO 	if (result->fp)
8836a50d4a4SMasatake YAMATO 		fclose (result->fp);
884ee45a800SMasatake YAMATO 	free (result);
885ee45a800SMasatake YAMATO 	info->status.opened = 0;
886ee45a800SMasatake YAMATO 	return NULL;
8874d9e7fc5SMasatake YAMATO }
8884d9e7fc5SMasatake YAMATO 
terminate(tagFile * const file)8894d9e7fc5SMasatake YAMATO static void terminate (tagFile *const file)
8904d9e7fc5SMasatake YAMATO {
8914d9e7fc5SMasatake YAMATO 	fclose (file->fp);
8924d9e7fc5SMasatake YAMATO 
8934d9e7fc5SMasatake YAMATO 	free (file->line.buffer);
8944d9e7fc5SMasatake YAMATO 	free (file->name.buffer);
8954d9e7fc5SMasatake YAMATO 	free (file->fields.list);
8964d9e7fc5SMasatake YAMATO 
8974d9e7fc5SMasatake YAMATO 	if (file->program.author != NULL)
8984d9e7fc5SMasatake YAMATO 		free (file->program.author);
8994d9e7fc5SMasatake YAMATO 	if (file->program.name != NULL)
9004d9e7fc5SMasatake YAMATO 		free (file->program.name);
9014d9e7fc5SMasatake YAMATO 	if (file->program.url != NULL)
9024d9e7fc5SMasatake YAMATO 		free (file->program.url);
9034d9e7fc5SMasatake YAMATO 	if (file->program.version != NULL)
9044d9e7fc5SMasatake YAMATO 		free (file->program.version);
9054d9e7fc5SMasatake YAMATO 	if (file->search.name != NULL)
9064d9e7fc5SMasatake YAMATO 		free (file->search.name);
9074d9e7fc5SMasatake YAMATO 
9084d9e7fc5SMasatake YAMATO 	memset (file, 0, sizeof (tagFile));
9094d9e7fc5SMasatake YAMATO 
9104d9e7fc5SMasatake YAMATO 	free (file);
9114d9e7fc5SMasatake YAMATO }
9124d9e7fc5SMasatake YAMATO 
readNext(tagFile * const file,tagEntry * const entry)9134d9e7fc5SMasatake YAMATO static tagResult readNext (tagFile *const file, tagEntry *const entry)
9144d9e7fc5SMasatake YAMATO {
9154d9e7fc5SMasatake YAMATO 	tagResult result;
9160e675dccSMasatake YAMATO 
9170e675dccSMasatake YAMATO 	if (file == NULL)
9180e675dccSMasatake YAMATO 		return TagFailure;
9190e675dccSMasatake YAMATO 
9200e675dccSMasatake YAMATO 	if (! file->initialized)
921a41b1651SMasatake YAMATO 	{
922a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
9230e675dccSMasatake YAMATO 		return TagFailure;
924a41b1651SMasatake YAMATO 	}
9250e675dccSMasatake YAMATO 
9260e675dccSMasatake YAMATO 	if (! readTagLine (file, &file->err))
9270e675dccSMasatake YAMATO 		return TagFailure;
9280e675dccSMasatake YAMATO 
929a41b1651SMasatake YAMATO 	result = (entry != NULL)
930a41b1651SMasatake YAMATO 		? parseTagLine (file, entry, &file->err)
931a41b1651SMasatake YAMATO 		: TagSuccess;
9320e675dccSMasatake YAMATO 
9334d9e7fc5SMasatake YAMATO 	return result;
9344d9e7fc5SMasatake YAMATO }
9354d9e7fc5SMasatake YAMATO 
readFieldValue(const tagEntry * const entry,const char * const key)9364d9e7fc5SMasatake YAMATO static const char *readFieldValue (
9374d9e7fc5SMasatake YAMATO 	const tagEntry *const entry, const char *const key)
9384d9e7fc5SMasatake YAMATO {
9394d9e7fc5SMasatake YAMATO 	const char *result = NULL;
9404d9e7fc5SMasatake YAMATO 	int i;
9414d9e7fc5SMasatake YAMATO 	if (strcmp (key, "kind") == 0)
9424d9e7fc5SMasatake YAMATO 		result = entry->kind;
9434d9e7fc5SMasatake YAMATO 	else if (strcmp (key, "file") == 0)
9444d9e7fc5SMasatake YAMATO 		result = EmptyString;
9454d9e7fc5SMasatake YAMATO 	else for (i = 0  ;  i < entry->fields.count  &&  result == NULL  ;  ++i)
9464d9e7fc5SMasatake YAMATO 		if (strcmp (entry->fields.list [i].key, key) == 0)
9474d9e7fc5SMasatake YAMATO 			result = entry->fields.list [i].value;
9484d9e7fc5SMasatake YAMATO 	return result;
9494d9e7fc5SMasatake YAMATO }
9504d9e7fc5SMasatake YAMATO 
readTagLineSeek(tagFile * const file,const rt_off_t pos)951*d5b81c4aSMasatake YAMATO static int readTagLineSeek (tagFile *const file, const rt_off_t pos)
9524d9e7fc5SMasatake YAMATO {
953*d5b81c4aSMasatake YAMATO 	if (readtags_fseek (file->fp, pos, SEEK_SET) < 0)
9544d9e7fc5SMasatake YAMATO 	{
955a41b1651SMasatake YAMATO 		file->err = errno;
956a41b1651SMasatake YAMATO 		return 0;
9574d9e7fc5SMasatake YAMATO 	}
958a41b1651SMasatake YAMATO 
959a41b1651SMasatake YAMATO 	/* read probable partial line */
960a41b1651SMasatake YAMATO 	if (!readTagLine (file, &file->err))
961a41b1651SMasatake YAMATO 		return 0;
962a41b1651SMasatake YAMATO 
963a41b1651SMasatake YAMATO 	/* read complete line */
964a41b1651SMasatake YAMATO 	if (pos > 0)
965a41b1651SMasatake YAMATO 		return readTagLine (file, &file->err);
966a41b1651SMasatake YAMATO 
967a41b1651SMasatake YAMATO 	return 1;
9684d9e7fc5SMasatake YAMATO }
9694d9e7fc5SMasatake YAMATO 
nameComparison(tagFile * const file)9704d9e7fc5SMasatake YAMATO static int nameComparison (tagFile *const file)
9714d9e7fc5SMasatake YAMATO {
9724d9e7fc5SMasatake YAMATO 	int result;
9734d9e7fc5SMasatake YAMATO 	if (file->search.ignorecase)
9744d9e7fc5SMasatake YAMATO 	{
9754d9e7fc5SMasatake YAMATO 		if (file->search.partial)
9764d9e7fc5SMasatake YAMATO 			result = tagnuppercmp (file->search.name, file->name.buffer,
9774d9e7fc5SMasatake YAMATO 					file->search.nameLength);
9784d9e7fc5SMasatake YAMATO 		else
9794d9e7fc5SMasatake YAMATO 			result = taguppercmp (file->search.name, file->name.buffer);
9804d9e7fc5SMasatake YAMATO 	}
9814d9e7fc5SMasatake YAMATO 	else
9824d9e7fc5SMasatake YAMATO 	{
9834d9e7fc5SMasatake YAMATO 		if (file->search.partial)
9844d9e7fc5SMasatake YAMATO 			result = tagncmp (file->search.name, file->name.buffer,
9854d9e7fc5SMasatake YAMATO 					file->search.nameLength);
9864d9e7fc5SMasatake YAMATO 		else
9874d9e7fc5SMasatake YAMATO 			result = tagcmp (file->search.name, file->name.buffer);
9884d9e7fc5SMasatake YAMATO 	}
9894d9e7fc5SMasatake YAMATO 	return result;
9904d9e7fc5SMasatake YAMATO }
9914d9e7fc5SMasatake YAMATO 
findFirstNonMatchBefore(tagFile * const file)992a41b1651SMasatake YAMATO static tagResult findFirstNonMatchBefore (tagFile *const file)
9934d9e7fc5SMasatake YAMATO {
9944d9e7fc5SMasatake YAMATO #define JUMP_BACK 512
9954d9e7fc5SMasatake YAMATO 	int more_lines;
9964d9e7fc5SMasatake YAMATO 	int comp;
997*d5b81c4aSMasatake YAMATO 	rt_off_t start = file->pos;
998*d5b81c4aSMasatake YAMATO 	rt_off_t pos = start;
9994d9e7fc5SMasatake YAMATO 	do
10004d9e7fc5SMasatake YAMATO 	{
1001*d5b81c4aSMasatake YAMATO 		if (pos < (rt_off_t) JUMP_BACK)
10024d9e7fc5SMasatake YAMATO 			pos = 0;
10034d9e7fc5SMasatake YAMATO 		else
10044d9e7fc5SMasatake YAMATO 			pos = pos - JUMP_BACK;
10054d9e7fc5SMasatake YAMATO 		more_lines = readTagLineSeek (file, pos);
1006a41b1651SMasatake YAMATO 		if (more_lines == 0 && file->err)
1007a41b1651SMasatake YAMATO 			return TagFailure;
10084d9e7fc5SMasatake YAMATO 		comp = nameComparison (file);
10094d9e7fc5SMasatake YAMATO 	} while (more_lines  &&  comp == 0  &&  pos > 0  &&  pos < start);
1010a41b1651SMasatake YAMATO 	return TagSuccess;
10114d9e7fc5SMasatake YAMATO }
10124d9e7fc5SMasatake YAMATO 
findFirstMatchBefore(tagFile * const file)10134d9e7fc5SMasatake YAMATO static tagResult findFirstMatchBefore (tagFile *const file)
10144d9e7fc5SMasatake YAMATO {
10154d9e7fc5SMasatake YAMATO 	tagResult result = TagFailure;
10164d9e7fc5SMasatake YAMATO 	int more_lines;
1017*d5b81c4aSMasatake YAMATO 	rt_off_t start = file->pos;
1018a41b1651SMasatake YAMATO 	if (findFirstNonMatchBefore (file) != TagSuccess)
1019a41b1651SMasatake YAMATO 		return TagFailure;
10204d9e7fc5SMasatake YAMATO 	do
10214d9e7fc5SMasatake YAMATO 	{
1022a41b1651SMasatake YAMATO 		more_lines = readTagLine (file, &file->err);
1023a41b1651SMasatake YAMATO 		if (more_lines == 0 && file->err)
1024a41b1651SMasatake YAMATO 			return TagFailure;
10254d9e7fc5SMasatake YAMATO 		if (nameComparison (file) == 0)
10264d9e7fc5SMasatake YAMATO 			result = TagSuccess;
10274d9e7fc5SMasatake YAMATO 	} while (more_lines  &&  result != TagSuccess  &&  file->pos < start);
10284d9e7fc5SMasatake YAMATO 	return result;
10294d9e7fc5SMasatake YAMATO }
10304d9e7fc5SMasatake YAMATO 
findBinary(tagFile * const file)10314d9e7fc5SMasatake YAMATO static tagResult findBinary (tagFile *const file)
10324d9e7fc5SMasatake YAMATO {
10334d9e7fc5SMasatake YAMATO 	tagResult result = TagFailure;
1034*d5b81c4aSMasatake YAMATO 	rt_off_t lower_limit = 0;
1035*d5b81c4aSMasatake YAMATO 	rt_off_t upper_limit = file->size;
1036*d5b81c4aSMasatake YAMATO 	rt_off_t last_pos = 0;
1037*d5b81c4aSMasatake YAMATO 	rt_off_t pos = upper_limit / 2;
10384d9e7fc5SMasatake YAMATO 	while (result != TagSuccess)
10394d9e7fc5SMasatake YAMATO 	{
10404d9e7fc5SMasatake YAMATO 		if (! readTagLineSeek (file, pos))
10414d9e7fc5SMasatake YAMATO 		{
1042a41b1651SMasatake YAMATO 			if (file->err)
1043a41b1651SMasatake YAMATO 				break;
10444d9e7fc5SMasatake YAMATO 			/* in case we fell off end of file */
10454d9e7fc5SMasatake YAMATO 			result = findFirstMatchBefore (file);
10464d9e7fc5SMasatake YAMATO 			break;
10474d9e7fc5SMasatake YAMATO 		}
10484d9e7fc5SMasatake YAMATO 		else if (pos == last_pos)
10494d9e7fc5SMasatake YAMATO 		{
10504d9e7fc5SMasatake YAMATO 			/* prevent infinite loop if we backed up to beginning of file */
10514d9e7fc5SMasatake YAMATO 			break;
10524d9e7fc5SMasatake YAMATO 		}
10534d9e7fc5SMasatake YAMATO 		else
10544d9e7fc5SMasatake YAMATO 		{
10554d9e7fc5SMasatake YAMATO 			const int comp = nameComparison (file);
10564d9e7fc5SMasatake YAMATO 			last_pos = pos;
10574d9e7fc5SMasatake YAMATO 			if (comp < 0)
10584d9e7fc5SMasatake YAMATO 			{
10594d9e7fc5SMasatake YAMATO 				upper_limit = pos;
10604d9e7fc5SMasatake YAMATO 				pos = lower_limit + ((upper_limit - lower_limit) / 2);
10614d9e7fc5SMasatake YAMATO 			}
10624d9e7fc5SMasatake YAMATO 			else if (comp > 0)
10634d9e7fc5SMasatake YAMATO 			{
10644d9e7fc5SMasatake YAMATO 				lower_limit = pos;
10654d9e7fc5SMasatake YAMATO 				pos = lower_limit + ((upper_limit - lower_limit) / 2);
10664d9e7fc5SMasatake YAMATO 			}
10674d9e7fc5SMasatake YAMATO 			else if (pos == 0)
10684d9e7fc5SMasatake YAMATO 				result = TagSuccess;
10694d9e7fc5SMasatake YAMATO 			else
1070a41b1651SMasatake YAMATO 			{
10714d9e7fc5SMasatake YAMATO 				result = findFirstMatchBefore (file);
1072a41b1651SMasatake YAMATO 				if (result != TagSuccess && file->err)
1073a41b1651SMasatake YAMATO 					break;
1074a41b1651SMasatake YAMATO 			}
10754d9e7fc5SMasatake YAMATO 		}
10764d9e7fc5SMasatake YAMATO 	}
10774d9e7fc5SMasatake YAMATO 	return result;
10784d9e7fc5SMasatake YAMATO }
10794d9e7fc5SMasatake YAMATO 
findSequentialFull(tagFile * const file,int (* isAcceptable)(tagFile * const,void *),void * data)1080ee45a800SMasatake YAMATO static tagResult findSequentialFull (tagFile *const file,
1081ee45a800SMasatake YAMATO 									 int (* isAcceptable) (tagFile *const, void *),
1082ee45a800SMasatake YAMATO 									 void *data)
10834d9e7fc5SMasatake YAMATO {
10840e675dccSMasatake YAMATO 	if (file == NULL)
10850e675dccSMasatake YAMATO 		return TagFailure;
10860e675dccSMasatake YAMATO 
10870e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
1088a41b1651SMasatake YAMATO 	{
1089a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1090a41b1651SMasatake YAMATO 		return TagFailure;
1091a41b1651SMasatake YAMATO 	}
1092a41b1651SMasatake YAMATO 
10934d9e7fc5SMasatake YAMATO 	tagResult result = TagFailure;
1094a41b1651SMasatake YAMATO 	while (result == TagFailure)
10954d9e7fc5SMasatake YAMATO 	{
1096a41b1651SMasatake YAMATO 		if (! readTagLine (file, &file->err))
1097a41b1651SMasatake YAMATO 			break;
1098ee45a800SMasatake YAMATO 		if (isAcceptable (file, data))
10994d9e7fc5SMasatake YAMATO 			result = TagSuccess;
11004d9e7fc5SMasatake YAMATO 	}
11014d9e7fc5SMasatake YAMATO 	return result;
11024d9e7fc5SMasatake YAMATO }
11034d9e7fc5SMasatake YAMATO 
nameAcceptable(tagFile * const file,void * unused)1104ee45a800SMasatake YAMATO static int nameAcceptable (tagFile *const file, void *unused)
1105ee45a800SMasatake YAMATO {
1106ee45a800SMasatake YAMATO 	return (nameComparison (file) == 0);
1107ee45a800SMasatake YAMATO }
1108ee45a800SMasatake YAMATO 
findSequential(tagFile * const file)1109ee45a800SMasatake YAMATO static tagResult findSequential (tagFile *const file)
1110ee45a800SMasatake YAMATO {
1111ee45a800SMasatake YAMATO 	return findSequentialFull (file, nameAcceptable, NULL);
1112ee45a800SMasatake YAMATO }
1113ee45a800SMasatake YAMATO 
find(tagFile * const file,tagEntry * const entry,const char * const name,const int options)11144d9e7fc5SMasatake YAMATO static tagResult find (tagFile *const file, tagEntry *const entry,
11154d9e7fc5SMasatake YAMATO 					   const char *const name, const int options)
11164d9e7fc5SMasatake YAMATO {
11174d9e7fc5SMasatake YAMATO 	tagResult result;
11184d9e7fc5SMasatake YAMATO 	if (file->search.name != NULL)
11194d9e7fc5SMasatake YAMATO 		free (file->search.name);
11204d9e7fc5SMasatake YAMATO 	file->search.name = duplicate (name);
1121a41b1651SMasatake YAMATO 	if (file->search.name == NULL)
1122a41b1651SMasatake YAMATO 	{
1123a41b1651SMasatake YAMATO 		file->err = ENOMEM;
1124a41b1651SMasatake YAMATO 		return TagFailure;
1125a41b1651SMasatake YAMATO 	}
11264d9e7fc5SMasatake YAMATO 	file->search.nameLength = strlen (name);
11274d9e7fc5SMasatake YAMATO 	file->search.partial = (options & TAG_PARTIALMATCH) != 0;
11284d9e7fc5SMasatake YAMATO 	file->search.ignorecase = (options & TAG_IGNORECASE) != 0;
1129*d5b81c4aSMasatake YAMATO 	if (readtags_fseek (file->fp, 0, SEEK_END) < 0)
1130a41b1651SMasatake YAMATO 	{
1131a41b1651SMasatake YAMATO 		file->err = errno;
1132a41b1651SMasatake YAMATO 		return TagFailure;
1133a41b1651SMasatake YAMATO 	}
1134*d5b81c4aSMasatake YAMATO 	file->size = readtags_ftell (file->fp);
1135a41b1651SMasatake YAMATO 	if (file->size == -1)
1136a41b1651SMasatake YAMATO 	{
1137a41b1651SMasatake YAMATO 		file->err = errno;
1138a41b1651SMasatake YAMATO 		return TagFailure;
1139a41b1651SMasatake YAMATO 	}
1140*d5b81c4aSMasatake YAMATO 	if (readtags_fseek(file->fp, 0, SEEK_SET) == -1)
1141a41b1651SMasatake YAMATO 	{
1142a41b1651SMasatake YAMATO 		file->err = errno;
1143a41b1651SMasatake YAMATO 		return TagFailure;
1144a41b1651SMasatake YAMATO 	}
11454d9e7fc5SMasatake YAMATO 	if ((file->sortMethod == TAG_SORTED      && !file->search.ignorecase) ||
11464d9e7fc5SMasatake YAMATO 		(file->sortMethod == TAG_FOLDSORTED  &&  file->search.ignorecase))
11474d9e7fc5SMasatake YAMATO 	{
11484d9e7fc5SMasatake YAMATO 		result = findBinary (file);
1149a41b1651SMasatake YAMATO 		if (result == TagFailure && file->err)
1150a41b1651SMasatake YAMATO 			return TagFailure;
11514d9e7fc5SMasatake YAMATO 	}
11524d9e7fc5SMasatake YAMATO 	else
11534d9e7fc5SMasatake YAMATO 	{
11544d9e7fc5SMasatake YAMATO 		result = findSequential (file);
1155a41b1651SMasatake YAMATO 		if (result == TagFailure && file->err)
1156a41b1651SMasatake YAMATO 			return TagFailure;
11574d9e7fc5SMasatake YAMATO 	}
11584d9e7fc5SMasatake YAMATO 
11594d9e7fc5SMasatake YAMATO 	if (result != TagSuccess)
11604d9e7fc5SMasatake YAMATO 		file->search.pos = file->size;
11614d9e7fc5SMasatake YAMATO 	else
11624d9e7fc5SMasatake YAMATO 	{
11634d9e7fc5SMasatake YAMATO 		file->search.pos = file->pos;
1164a41b1651SMasatake YAMATO 		result = (entry != NULL)
1165a41b1651SMasatake YAMATO 			? parseTagLine (file, entry, &file->err)
1166a41b1651SMasatake YAMATO 			: TagSuccess;
11674d9e7fc5SMasatake YAMATO 	}
11684d9e7fc5SMasatake YAMATO 	return result;
11694d9e7fc5SMasatake YAMATO }
11704d9e7fc5SMasatake YAMATO 
findNextFull(tagFile * const file,tagEntry * const entry,int sorted,int (* isAcceptable)(tagFile * const,void *),void * data)1171ee45a800SMasatake YAMATO static tagResult findNextFull (tagFile *const file, tagEntry *const entry,
1172ee45a800SMasatake YAMATO 							   int sorted,
1173ee45a800SMasatake YAMATO 							   int (* isAcceptable) (tagFile *const, void *),
1174ee45a800SMasatake YAMATO 							   void *data)
11754d9e7fc5SMasatake YAMATO {
11764d9e7fc5SMasatake YAMATO 	tagResult result;
1177ee45a800SMasatake YAMATO 	if (sorted)
11784d9e7fc5SMasatake YAMATO 	{
11794d9e7fc5SMasatake YAMATO 		result = tagsNext (file, entry);
1180ee45a800SMasatake YAMATO 		if (result == TagSuccess  && !isAcceptable (file, data))
11814d9e7fc5SMasatake YAMATO 			result = TagFailure;
11824d9e7fc5SMasatake YAMATO 	}
11834d9e7fc5SMasatake YAMATO 	else
11844d9e7fc5SMasatake YAMATO 	{
1185ee45a800SMasatake YAMATO 		result = findSequentialFull (file, isAcceptable, data);
11864d9e7fc5SMasatake YAMATO 		if (result == TagSuccess  &&  entry != NULL)
1187a41b1651SMasatake YAMATO 			result = parseTagLine (file, entry, &file->err);
11884d9e7fc5SMasatake YAMATO 	}
11894d9e7fc5SMasatake YAMATO 	return result;
11904d9e7fc5SMasatake YAMATO }
11914d9e7fc5SMasatake YAMATO 
findNext(tagFile * const file,tagEntry * const entry)1192ee45a800SMasatake YAMATO static tagResult findNext (tagFile *const file, tagEntry *const entry)
1193ee45a800SMasatake YAMATO {
1194ee45a800SMasatake YAMATO 	return findNextFull (file, entry,
1195ee45a800SMasatake YAMATO 						 (file->sortMethod == TAG_SORTED      && !file->search.ignorecase) ||
1196ee45a800SMasatake YAMATO 						 (file->sortMethod == TAG_FOLDSORTED  &&  file->search.ignorecase),
1197ee45a800SMasatake YAMATO 						 nameAcceptable, NULL);
1198ee45a800SMasatake YAMATO }
1199ee45a800SMasatake YAMATO 
findPseudoTag(tagFile * const file,int rewindBeforeFinding,tagEntry * const entry)1200ee45a800SMasatake YAMATO static tagResult findPseudoTag (tagFile *const file, int rewindBeforeFinding, tagEntry *const entry)
1201ee45a800SMasatake YAMATO {
12020e675dccSMasatake YAMATO 	if (file == NULL)
12030e675dccSMasatake YAMATO 		return TagFailure;
12040e675dccSMasatake YAMATO 
12050e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
1206ee45a800SMasatake YAMATO 	{
12070e675dccSMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1208a41b1651SMasatake YAMATO 		return TagFailure;
1209a41b1651SMasatake YAMATO 	}
1210a41b1651SMasatake YAMATO 
1211ee45a800SMasatake YAMATO 	if (rewindBeforeFinding)
1212a41b1651SMasatake YAMATO 	{
1213*d5b81c4aSMasatake YAMATO 		if (readtags_fseek(file->fp, 0, SEEK_SET) == -1)
1214a41b1651SMasatake YAMATO 		{
1215a41b1651SMasatake YAMATO 			file->err = errno;
1216a41b1651SMasatake YAMATO 			return TagFailure;
1217a41b1651SMasatake YAMATO 		}
1218a41b1651SMasatake YAMATO 	}
1219a41b1651SMasatake YAMATO 	return findNextFull (file, entry,
1220ee45a800SMasatake YAMATO 						 (file->sortMethod == TAG_SORTED || file->sortMethod == TAG_FOLDSORTED),
1221ee45a800SMasatake YAMATO 						 doesFilePointPseudoTag,
1222ee45a800SMasatake YAMATO 						 NULL);
1223ee45a800SMasatake YAMATO }
1224ee45a800SMasatake YAMATO 
1225ee45a800SMasatake YAMATO 
12264d9e7fc5SMasatake YAMATO /*
12274d9e7fc5SMasatake YAMATO *  EXTERNAL INTERFACE
12284d9e7fc5SMasatake YAMATO */
12294d9e7fc5SMasatake YAMATO 
tagsOpen(const char * const filePath,tagFileInfo * const info)12304d9e7fc5SMasatake YAMATO extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info)
12314d9e7fc5SMasatake YAMATO {
1232a41b1651SMasatake YAMATO 	tagFileInfo infoDummy;
1233a41b1651SMasatake YAMATO 	return initialize (filePath, info? info: &infoDummy);
12344d9e7fc5SMasatake YAMATO }
12354d9e7fc5SMasatake YAMATO 
tagsSetSortType(tagFile * const file,const tagSortType type)1236a41b1651SMasatake YAMATO extern tagResult tagsSetSortType (tagFile *const file, const tagSortType type)
12374d9e7fc5SMasatake YAMATO {
12380e675dccSMasatake YAMATO 	if (file == NULL)
12390e675dccSMasatake YAMATO 		return TagFailure;
12400e675dccSMasatake YAMATO 
12410e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
12424d9e7fc5SMasatake YAMATO 	{
1243a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1244a41b1651SMasatake YAMATO 		return TagFailure;
12454d9e7fc5SMasatake YAMATO 	}
1246a41b1651SMasatake YAMATO 
1247a41b1651SMasatake YAMATO 	switch (type)
1248a41b1651SMasatake YAMATO 	{
1249a41b1651SMasatake YAMATO 	case TAG_UNSORTED:
1250a41b1651SMasatake YAMATO 	case TAG_SORTED:
1251a41b1651SMasatake YAMATO 	case TAG_FOLDSORTED:
1252a41b1651SMasatake YAMATO 		file->sortMethod = type;
1253a41b1651SMasatake YAMATO 		return TagSuccess;
1254a41b1651SMasatake YAMATO 	default:
1255a41b1651SMasatake YAMATO 		file->err = TagErrnoUnexpectedSortedMethod;
1256a41b1651SMasatake YAMATO 		return TagFailure;
1257a41b1651SMasatake YAMATO 	}
12584d9e7fc5SMasatake YAMATO }
12594d9e7fc5SMasatake YAMATO 
tagsFirst(tagFile * const file,tagEntry * const entry)12604d9e7fc5SMasatake YAMATO extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry)
12614d9e7fc5SMasatake YAMATO {
12620e675dccSMasatake YAMATO 	if (file == NULL)
12630e675dccSMasatake YAMATO 		return TagFailure;
12640e675dccSMasatake YAMATO 
12650e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
12664d9e7fc5SMasatake YAMATO 	{
1267a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1268a41b1651SMasatake YAMATO 		return TagFailure;
12694d9e7fc5SMasatake YAMATO 	}
1270a41b1651SMasatake YAMATO 
1271a41b1651SMasatake YAMATO 	if (gotoFirstLogicalTag (file) != TagSuccess)
1272a41b1651SMasatake YAMATO 		return TagFailure;
1273a41b1651SMasatake YAMATO 	return readNext (file, entry);
12744d9e7fc5SMasatake YAMATO }
12754d9e7fc5SMasatake YAMATO 
tagsNext(tagFile * const file,tagEntry * const entry)12764d9e7fc5SMasatake YAMATO extern tagResult tagsNext (tagFile *const file, tagEntry *const entry)
12774d9e7fc5SMasatake YAMATO {
12780e675dccSMasatake YAMATO 	if (file == NULL)
12790e675dccSMasatake YAMATO 		return TagFailure;
12800e675dccSMasatake YAMATO 
12810e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
1282a41b1651SMasatake YAMATO 	{
1283a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1284a41b1651SMasatake YAMATO 		return TagFailure;
1285a41b1651SMasatake YAMATO 	}
1286a41b1651SMasatake YAMATO 
1287a41b1651SMasatake YAMATO 	return readNext (file, entry);
12884d9e7fc5SMasatake YAMATO }
12894d9e7fc5SMasatake YAMATO 
tagsField(const tagEntry * const entry,const char * const key)12904d9e7fc5SMasatake YAMATO extern const char *tagsField (const tagEntry *const entry, const char *const key)
12914d9e7fc5SMasatake YAMATO {
12924d9e7fc5SMasatake YAMATO 	const char *result = NULL;
12934d9e7fc5SMasatake YAMATO 	if (entry != NULL)
12944d9e7fc5SMasatake YAMATO 		result = readFieldValue (entry, key);
12954d9e7fc5SMasatake YAMATO 	return result;
12964d9e7fc5SMasatake YAMATO }
12974d9e7fc5SMasatake YAMATO 
tagsFind(tagFile * const file,tagEntry * const entry,const char * const name,const int options)12984d9e7fc5SMasatake YAMATO extern tagResult tagsFind (tagFile *const file, tagEntry *const entry,
12994d9e7fc5SMasatake YAMATO 						   const char *const name, const int options)
13004d9e7fc5SMasatake YAMATO {
13010e675dccSMasatake YAMATO 	if (file == NULL)
13020e675dccSMasatake YAMATO 		return TagFailure;
13030e675dccSMasatake YAMATO 
13040e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
1305a41b1651SMasatake YAMATO 	{
1306a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1307a41b1651SMasatake YAMATO 		return TagFailure;
1308a41b1651SMasatake YAMATO 	}
13090e675dccSMasatake YAMATO 
1310a41b1651SMasatake YAMATO 	return find (file, entry, name, options);
13114d9e7fc5SMasatake YAMATO }
13124d9e7fc5SMasatake YAMATO 
tagsFindNext(tagFile * const file,tagEntry * const entry)13134d9e7fc5SMasatake YAMATO extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry)
13144d9e7fc5SMasatake YAMATO {
13150e675dccSMasatake YAMATO 	if (file == NULL)
13160e675dccSMasatake YAMATO 		return TagFailure;
13170e675dccSMasatake YAMATO 
13180e675dccSMasatake YAMATO 	if (!file->initialized || file->err)
1319a41b1651SMasatake YAMATO 	{
1320a41b1651SMasatake YAMATO 		file->err = TagErrnoInvalidArgument;
1321a41b1651SMasatake YAMATO 		return TagFailure;
1322a41b1651SMasatake YAMATO 	}
13230e675dccSMasatake YAMATO 
1324a41b1651SMasatake YAMATO 	return findNext (file, entry);
13254d9e7fc5SMasatake YAMATO }
13264d9e7fc5SMasatake YAMATO 
tagsFirstPseudoTag(tagFile * const file,tagEntry * const entry)1327ee45a800SMasatake YAMATO extern tagResult tagsFirstPseudoTag (tagFile *const file, tagEntry *const entry)
1328ee45a800SMasatake YAMATO {
1329ee45a800SMasatake YAMATO 	return findPseudoTag (file, 1, entry);
1330ee45a800SMasatake YAMATO }
1331ee45a800SMasatake YAMATO 
tagsNextPseudoTag(tagFile * const file,tagEntry * const entry)1332ee45a800SMasatake YAMATO extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry)
1333ee45a800SMasatake YAMATO {
1334ee45a800SMasatake YAMATO 	return findPseudoTag (file, 0, entry);
1335ee45a800SMasatake YAMATO }
1336ee45a800SMasatake YAMATO 
tagsClose(tagFile * const file)13374d9e7fc5SMasatake YAMATO extern tagResult tagsClose (tagFile *const file)
13384d9e7fc5SMasatake YAMATO {
13394d9e7fc5SMasatake YAMATO 	tagResult result = TagFailure;
13404d9e7fc5SMasatake YAMATO 	if (file != NULL  &&  file->initialized)
13414d9e7fc5SMasatake YAMATO 	{
13424d9e7fc5SMasatake YAMATO 		terminate (file);
13434d9e7fc5SMasatake YAMATO 		result = TagSuccess;
13444d9e7fc5SMasatake YAMATO 	}
13454d9e7fc5SMasatake YAMATO 	return result;
13464d9e7fc5SMasatake YAMATO }
1347a41b1651SMasatake YAMATO 
tagsGetErrno(tagFile * const file)1348a41b1651SMasatake YAMATO extern int tagsGetErrno (tagFile *const file)
1349a41b1651SMasatake YAMATO {
1350a41b1651SMasatake YAMATO 	if (file == NULL)
1351a41b1651SMasatake YAMATO 		return TagErrnoInvalidArgument;
1352a41b1651SMasatake YAMATO 	return file->err;
1353a41b1651SMasatake YAMATO }
1354