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