2 * Copyright (c) 1996-2003, Darren Hiebert
4 * This source code is released into the public domain.
6 * This module contains functions for reading tag files.
17 #include <sys/types.h> /* to declare off_t */
35 /* Information about current tag file */
37 /* has the file been opened and this structure initialized? */
39 /* format of tag file */
41 /* how is the tag file sorted? */
42 tagSortType sortMethod
;
43 /* pointer to file structure */
45 /* file position of first character of `line' */
47 /* size of tag file in seekable positions */
51 /* name of tag in last line read */
53 /* defines tag search state */
55 /* file position of last match for tag */
57 /* name of tag last searched for */
59 /* length of name for partial matches */
61 /* performing partial match */
66 /* miscellaneous extension fields */
68 /* number of entries in `list' */
70 /* list of key value pairs */
71 tagExtensionField
*list
;
73 /* buffers to be freed at close */
75 /* name of program author */
79 /* URL of distribution */
84 /* 0 (initial state set by calloc), errno value,
85 * or tagErrno typed value */
92 static const char *const EmptyString
= "";
93 static const char *const PseudoTagPrefix
= "!_";
94 static const size_t PseudoTagPrefixLength
= 2;
97 * FUNCTION DEFINITIONS
100 /* Converts a hexadecimal digit to its value */
101 static int xdigitValue (char digit
)
103 if (digit
>= '0' && digit
<= '9')
105 else if (digit
>= 'a' && digit
<= 'f')
106 return 10 + digit
- 'a';
107 else if (digit
>= 'A' && digit
<= 'F')
108 return 10 + digit
- 'A';
114 * Reads the first character from the string, possibly un-escaping it, and
115 * advances *s to the start of the next character.
117 static int readTagCharacter (const char **s
)
119 int c
= **(const unsigned char **)s
;
127 case 't': c
= '\t'; (*s
)++; break;
128 case 'r': c
= '\r'; (*s
)++; break;
129 case 'n': c
= '\n'; (*s
)++; break;
130 case '\\': c
= '\\'; (*s
)++; break;
131 /* Universal-CTags extensions */
132 case 'a': c
= '\a'; (*s
)++; break;
133 case 'b': c
= '\b'; (*s
)++; break;
134 case 'v': c
= '\v'; (*s
)++; break;
135 case 'f': c
= '\f'; (*s
)++; break;
137 if (isxdigit ((*s
)[1]) && isxdigit ((*s
)[2]))
139 int val
= (xdigitValue ((*s
)[1]) << 4) | xdigitValue ((*s
)[2]);
154 * Compare two strings, ignoring case.
155 * Return 0 for match, < 0 for smaller, > 0 for bigger
156 * Make sure case is folded to uppercase in comparison (like for 'sort -f')
157 * This makes a difference when one of the chars lies between upper and lower
158 * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
160 static int taguppercmp (const char *s1
, const char *s2
)
166 c1
= (unsigned char)*s1
++;
167 c2
= readTagCharacter (&s2
);
169 result
= toupper (c1
) - toupper (c2
);
170 } while (result
== 0 && c1
!= '\0' && c2
!= '\0');
174 static int tagnuppercmp (const char *s1
, const char *s2
, size_t n
)
180 c1
= (unsigned char)*s1
++;
181 c2
= readTagCharacter (&s2
);
183 result
= toupper (c1
) - toupper (c2
);
184 } while (result
== 0 && --n
> 0 && c1
!= '\0' && c2
!= '\0');
188 static int tagcmp (const char *s1
, const char *s2
)
194 c1
= (unsigned char)*s1
++;
195 c2
= readTagCharacter (&s2
);
198 } while (result
== 0 && c1
!= '\0' && c2
!= '\0');
202 static int tagncmp (const char *s1
, const char *s2
, size_t n
)
209 c2
= readTagCharacter (&s2
);
212 } while (result
== 0 && --n
> 0 && c1
!= '\0' && c2
!= '\0');
216 static tagResult
growString (vstring
*s
)
218 tagResult result
= TagFailure
;
224 newLine
= (char*) malloc (newLength
);
230 newLength
= 2 * s
->size
;
231 newLine
= (char*) realloc (s
->buffer
, newLength
);
234 perror ("string too large");
244 /* Copy name of tag out of tag line */
245 static tagResult
copyName (tagFile
*const file
)
248 const char *end
= strchr (file
->line
.buffer
, '\t');
251 end
= strchr (file
->line
.buffer
, '\n');
253 end
= strchr (file
->line
.buffer
, '\r');
256 length
= end
- file
->line
.buffer
;
258 length
= strlen (file
->line
.buffer
);
259 while (length
>= file
->name
.size
)
261 if (growString (&file
->name
) != TagSuccess
)
264 strncpy (file
->name
.buffer
, file
->line
.buffer
, length
);
265 file
->name
.buffer
[length
] = '\0';
269 /* Return 1 on success.
270 * Return 0 on failure or EOF.
271 * errno is set to *err unless EOF.
273 static int readTagLineRaw (tagFile
*const file
, int *err
)
278 /* If reading the line places any character other than a null or a
279 * newline at the last character position in the buffer (one less than
280 * the buffer size), then we must resize the buffer and reattempt to read
285 char *const pLastChar
= file
->line
.buffer
+ file
->line
.size
- 2;
288 file
->pos
= ftell (file
->fp
);
297 line
= fgets (file
->line
.buffer
, (int) file
->line
.size
, file
->fp
);
302 if (! feof (file
->fp
))
306 else if (*pLastChar
!= '\0' &&
307 *pLastChar
!= '\n' && *pLastChar
!= '\r')
309 /* buffer overflow */
310 if (growString (&file
->line
) != TagSuccess
)
315 if (fseek (file
->fp
, file
->pos
, SEEK_SET
) < 0)
324 size_t i
= strlen (file
->line
.buffer
);
326 (file
->line
.buffer
[i
- 1] == '\n' || file
->line
.buffer
[i
- 1] == '\r'))
328 file
->line
.buffer
[i
- 1] = '\0';
332 } while (reReadLine
&& result
);
335 if (copyName (file
) != TagSuccess
)
344 /* Return 1 on success.
345 * Return 0 on failure or EOF.
346 * errno is set to *err unless EOF.
348 static int readTagLine (tagFile
*const file
, int *err
)
353 result
= readTagLineRaw (file
, err
);
354 } while (result
&& *file
->name
.buffer
== '\0');
358 static tagResult
growFields (tagFile
*const file
)
360 tagResult result
= TagFailure
;
361 unsigned short newCount
= (unsigned short) 2 * file
->fields
.max
;
362 tagExtensionField
*newFields
= (tagExtensionField
*)
363 realloc (file
->fields
.list
, newCount
* sizeof (tagExtensionField
));
364 if (newFields
== NULL
)
365 perror ("too many extension fields");
368 file
->fields
.list
= newFields
;
369 file
->fields
.max
= newCount
;
375 static tagResult
parseExtensionFields (tagFile
*const file
, tagEntry
*const entry
,
376 char *const string
, int *err
)
379 char *tail
= string
+ (string
? strlen(string
):0);
382 while (p
!= NULL
&& *p
!= '\0')
393 colon
= strchr (field
, ':');
398 const char *key
= field
;
400 const char *value
= q
;
401 const int key_len
= colon
- key
;
409 const char *next
= q
;
410 int ch
= readTagCharacter (&next
);
411 size_t skip
= next
- q
;
418 /* + 1 is for moving the area including the last '\0'. */
419 memmove (q
, next
, q_len
+ 1);
429 if (memcmp (key
, "kind", 4) == 0)
431 else if (memcmp (key
, "file", 4) == 0)
432 entry
->fileScope
= 1;
433 else if (memcmp (key
, "line", 4) == 0)
436 long m
= strtol (value
, &endptr
, 10);
437 if (*endptr
!= '\0' || m
< 0)
439 *err
= TagErrnoUnexpectedLineno
;
442 entry
->address
.lineNumber
= m
;
450 if (entry
->fields
.count
== file
->fields
.max
)
452 if (growFields (file
) != TagSuccess
)
458 file
->fields
.list
[entry
->fields
.count
].key
= key
;
459 file
->fields
.list
[entry
->fields
.count
].value
= value
;
460 ++entry
->fields
.count
;
468 static int isOdd (unsigned int i
)
473 static unsigned int countContinuousBackslashesBackward(const char *from
,
476 unsigned int counter
= 0;
478 for (; from
> till
; from
--)
488 static tagResult
parseTagLine (tagFile
*file
, tagEntry
*const entry
, int *err
)
491 char *p
= file
->line
.buffer
;
492 size_t p_len
= strlen (p
);
493 char *tab
= strchr (p
, TAB
);
495 memset(entry
, 0, sizeof(*entry
));
503 /* When unescaping, the input string becomes shorter.
504 * e.g. \t occupies two bytes on the tag file.
505 * It is converted to 0x9 and occupies one byte.
506 * memmove called here for shortening the line
510 const char *next
= p
;
511 int ch
= readTagCharacter (&next
);
512 size_t skip
= next
- p
;
519 /* + 1 is for moving the area including the last '\0'. */
520 memmove (p
, next
, p_len
+ 1);
530 tab
= strchr (p
, TAB
);
537 if (*p
== '/' || *p
== '?')
540 int delimiter
= *(unsigned char*) p
;
541 entry
->address
.lineNumber
= 0;
542 entry
->address
.pattern
= p
;
545 p
= strchr (p
+ 1, delimiter
);
547 && isOdd (countContinuousBackslashesBackward (p
- 1,
548 entry
->address
.pattern
)));
552 /* TODO: invalid pattern */
557 else if (isdigit ((int) *(unsigned char*) p
))
559 /* parse line number */
560 entry
->address
.pattern
= p
;
561 entry
->address
.lineNumber
= atol (p
);
562 while (isdigit ((int) *(unsigned char*) p
))
566 combinedPattern
= (strncmp (p
, ";/", 2) == 0) ||
567 (strncmp (p
, ";?", 2) == 0);
572 int delimiter
= *(unsigned char*) p
;
575 p
= strchr (p
+ 1, delimiter
);
577 && isOdd (countContinuousBackslashesBackward (p
- 1,
578 entry
->address
.pattern
)));
582 /* TODO: invalid pattern */
591 /* TODO: invalid pattern */
596 fieldsPresent
= (strncmp (p
, ";\"", 2) == 0);
600 if (parseExtensionFields (file
, entry
, p
+ 2, err
) != TagSuccess
)
606 if (entry
->fields
.count
> 0)
607 entry
->fields
.list
= file
->fields
.list
;
608 for (i
= entry
->fields
.count
; i
< file
->fields
.max
; ++i
)
610 file
->fields
.list
[i
].key
= NULL
;
611 file
->fields
.list
[i
].value
= NULL
;
616 static char *duplicate (const char *str
)
621 result
= strdup (str
);
628 static int isPseudoTagLine (const char *buffer
)
630 return (strncmp (buffer
, PseudoTagPrefix
, PseudoTagPrefixLength
) == 0);
633 static tagResult
readPseudoTags (tagFile
*const file
, tagFileInfo
*const info
)
637 tagResult result
= TagSuccess
;
638 const size_t prefixLength
= strlen (PseudoTagPrefix
);
640 info
->file
.format
= 1;
641 info
->file
.sort
= TAG_UNSORTED
;
642 info
->program
.author
= NULL
;
643 info
->program
.name
= NULL
;
644 info
->program
.url
= NULL
;
645 info
->program
.version
= NULL
;
649 if (fgetpos (file
->fp
, &startOfLine
) < 0)
654 if (! readTagLine (file
, &err
))
656 if (!isPseudoTagLine (file
->line
.buffer
))
661 const char *key
, *value
;
662 if (parseTagLine (file
, &entry
, &err
) != TagSuccess
)
664 key
= entry
.name
+ prefixLength
;
666 if (strcmp (key
, "TAG_FILE_SORTED") == 0)
669 long m
= strtol (value
, &endptr
, 10);
670 if (*endptr
!= '\0' || m
< 0 || m
> 2)
672 err
= TagErrnoUnexpectedSortedMethod
;
675 file
->sortMethod
= (tagSortType
) m
;
677 else if (strcmp (key
, "TAG_FILE_FORMAT") == 0)
680 long m
= strtol (value
, &endptr
, 10);
681 if (*endptr
!= '\0' || m
< 1 || m
> 2)
683 err
= TagErrnoUnexpectedFormat
;
686 file
->format
= (short) m
;
688 else if (strcmp (key
, "TAG_PROGRAM_AUTHOR") == 0)
690 file
->program
.author
= duplicate (value
);
691 if (value
&& file
->program
.author
== NULL
)
697 else if (strcmp (key
, "TAG_PROGRAM_NAME") == 0)
699 file
->program
.name
= duplicate (value
);
700 if (value
&& file
->program
.name
== NULL
)
706 else if (strcmp (key
, "TAG_PROGRAM_URL") == 0)
708 file
->program
.url
= duplicate (value
);
709 if (value
&& file
->program
.url
== NULL
)
715 else if (strcmp (key
, "TAG_PROGRAM_VERSION") == 0)
717 file
->program
.version
= duplicate (value
);
718 if (value
&& file
->program
.version
== NULL
)
725 info
->file
.format
= file
->format
;
726 info
->file
.sort
= file
->sortMethod
;
727 info
->program
.author
= file
->program
.author
;
728 info
->program
.name
= file
->program
.name
;
729 info
->program
.url
= file
->program
.url
;
730 info
->program
.version
= file
->program
.version
;
733 if (fsetpos (file
->fp
, &startOfLine
) < 0)
736 info
->status
.error_number
= err
;
742 static int doesFilePointPseudoTag (tagFile
*const file
, void *unused
)
744 return isPseudoTagLine (file
->name
.buffer
);
747 static tagResult
gotoFirstLogicalTag (tagFile
*const file
)
751 if (fseek(file
->fp
, 0L, SEEK_SET
) == -1)
759 if (fgetpos (file
->fp
, &startOfLine
) < 0)
764 if (! readTagLine (file
, &file
->err
))
770 if (!isPseudoTagLine (file
->line
.buffer
))
773 if (fsetpos (file
->fp
, &startOfLine
) < 0)
781 static tagFile
*initialize (const char *const filePath
, tagFileInfo
*const info
)
783 tagFile
*result
= (tagFile
*) calloc ((size_t) 1, sizeof (tagFile
));
787 info
->status
.opened
= 0;
788 info
->status
.error_number
= ENOMEM
;
792 if (growString (&result
->line
) != TagSuccess
)
794 if (growString (&result
->name
) != TagSuccess
)
796 result
->fields
.max
= 20;
797 result
->fields
.list
= (tagExtensionField
*) calloc (
798 result
->fields
.max
, sizeof (tagExtensionField
));
799 if (result
->fields
.list
== NULL
)
801 result
->fp
= fopen (filePath
, "rb");
802 if (result
->fp
== NULL
)
804 info
->status
.error_number
= errno
;
809 if (fseek (result
->fp
, 0, SEEK_END
) == -1)
811 info
->status
.error_number
= errno
;
814 result
->size
= ftell (result
->fp
);
815 if (result
->size
== -1)
817 info
->status
.error_number
= errno
;
820 if (fseek(result
->fp
, 0L, SEEK_SET
) == -1)
822 info
->status
.error_number
= errno
;
826 if (readPseudoTags (result
, info
) == TagFailure
)
829 info
->status
.opened
= 1;
830 result
->initialized
= 1;
834 info
->status
.error_number
= ENOMEM
;
836 free (result
->line
.buffer
);
837 free (result
->name
.buffer
);
838 free (result
->fields
.list
);
842 info
->status
.opened
= 0;
846 static void terminate (tagFile
*const file
)
850 free (file
->line
.buffer
);
851 free (file
->name
.buffer
);
852 free (file
->fields
.list
);
854 if (file
->program
.author
!= NULL
)
855 free (file
->program
.author
);
856 if (file
->program
.name
!= NULL
)
857 free (file
->program
.name
);
858 if (file
->program
.url
!= NULL
)
859 free (file
->program
.url
);
860 if (file
->program
.version
!= NULL
)
861 free (file
->program
.version
);
862 if (file
->search
.name
!= NULL
)
863 free (file
->search
.name
);
865 memset (file
, 0, sizeof (tagFile
));
870 static tagResult
readNext (tagFile
*const file
, tagEntry
*const entry
)
877 if (! file
->initialized
)
879 file
->err
= TagErrnoInvalidArgument
;
883 if (! readTagLine (file
, &file
->err
))
886 result
= (entry
!= NULL
)
887 ? parseTagLine (file
, entry
, &file
->err
)
893 static const char *readFieldValue (
894 const tagEntry
*const entry
, const char *const key
)
896 const char *result
= NULL
;
898 if (strcmp (key
, "kind") == 0)
899 result
= entry
->kind
;
900 else if (strcmp (key
, "file") == 0)
901 result
= EmptyString
;
902 else for (i
= 0 ; i
< entry
->fields
.count
&& result
== NULL
; ++i
)
903 if (strcmp (entry
->fields
.list
[i
].key
, key
) == 0)
904 result
= entry
->fields
.list
[i
].value
;
908 static int readTagLineSeek (tagFile
*const file
, const off_t pos
)
910 if (fseek (file
->fp
, pos
, SEEK_SET
) < 0)
916 /* read probable partial line */
917 if (!readTagLine (file
, &file
->err
))
920 /* read complete line */
922 return readTagLine (file
, &file
->err
);
927 static int nameComparison (tagFile
*const file
)
930 if (file
->search
.ignorecase
)
932 if (file
->search
.partial
)
933 result
= tagnuppercmp (file
->search
.name
, file
->name
.buffer
,
934 file
->search
.nameLength
);
936 result
= taguppercmp (file
->search
.name
, file
->name
.buffer
);
940 if (file
->search
.partial
)
941 result
= tagncmp (file
->search
.name
, file
->name
.buffer
,
942 file
->search
.nameLength
);
944 result
= tagcmp (file
->search
.name
, file
->name
.buffer
);
949 static tagResult
findFirstNonMatchBefore (tagFile
*const file
)
951 #define JUMP_BACK 512
954 off_t start
= file
->pos
;
958 if (pos
< (off_t
) JUMP_BACK
)
961 pos
= pos
- JUMP_BACK
;
962 more_lines
= readTagLineSeek (file
, pos
);
963 if (more_lines
== 0 && file
->err
)
965 comp
= nameComparison (file
);
966 } while (more_lines
&& comp
== 0 && pos
> 0 && pos
< start
);
970 static tagResult
findFirstMatchBefore (tagFile
*const file
)
972 tagResult result
= TagFailure
;
974 off_t start
= file
->pos
;
975 if (findFirstNonMatchBefore (file
) != TagSuccess
)
979 more_lines
= readTagLine (file
, &file
->err
);
980 if (more_lines
== 0 && file
->err
)
982 if (nameComparison (file
) == 0)
984 } while (more_lines
&& result
!= TagSuccess
&& file
->pos
< start
);
988 static tagResult
findBinary (tagFile
*const file
)
990 tagResult result
= TagFailure
;
991 off_t lower_limit
= 0;
992 off_t upper_limit
= file
->size
;
994 off_t pos
= upper_limit
/ 2;
995 while (result
!= TagSuccess
)
997 if (! readTagLineSeek (file
, pos
))
1001 /* in case we fell off end of file */
1002 result
= findFirstMatchBefore (file
);
1005 else if (pos
== last_pos
)
1007 /* prevent infinite loop if we backed up to beginning of file */
1012 const int comp
= nameComparison (file
);
1017 pos
= lower_limit
+ ((upper_limit
- lower_limit
) / 2);
1022 pos
= lower_limit
+ ((upper_limit
- lower_limit
) / 2);
1025 result
= TagSuccess
;
1028 result
= findFirstMatchBefore (file
);
1029 if (result
!= TagSuccess
&& file
->err
)
1037 static tagResult
findSequentialFull (tagFile
*const file
,
1038 int (* isAcceptable
) (tagFile
*const, void *),
1044 if (!file
->initialized
|| file
->err
)
1046 file
->err
= TagErrnoInvalidArgument
;
1050 tagResult result
= TagFailure
;
1051 while (result
== TagFailure
)
1053 if (! readTagLine (file
, &file
->err
))
1055 if (isAcceptable (file
, data
))
1056 result
= TagSuccess
;
1061 static int nameAcceptable (tagFile
*const file
, void *unused
)
1063 return (nameComparison (file
) == 0);
1066 static tagResult
findSequential (tagFile
*const file
)
1068 return findSequentialFull (file
, nameAcceptable
, NULL
);
1071 static tagResult
find (tagFile
*const file
, tagEntry
*const entry
,
1072 const char *const name
, const int options
)
1075 if (file
->search
.name
!= NULL
)
1076 free (file
->search
.name
);
1077 file
->search
.name
= duplicate (name
);
1078 if (file
->search
.name
== NULL
)
1083 file
->search
.nameLength
= strlen (name
);
1084 file
->search
.partial
= (options
& TAG_PARTIALMATCH
) != 0;
1085 file
->search
.ignorecase
= (options
& TAG_IGNORECASE
) != 0;
1086 if (fseek (file
->fp
, 0, SEEK_END
) < 0)
1091 file
->size
= ftell (file
->fp
);
1092 if (file
->size
== -1)
1097 if (fseek(file
->fp
, 0L, SEEK_SET
) == -1)
1102 if ((file
->sortMethod
== TAG_SORTED
&& !file
->search
.ignorecase
) ||
1103 (file
->sortMethod
== TAG_FOLDSORTED
&& file
->search
.ignorecase
))
1105 result
= findBinary (file
);
1106 if (result
== TagFailure
&& file
->err
)
1111 result
= findSequential (file
);
1112 if (result
== TagFailure
&& file
->err
)
1116 if (result
!= TagSuccess
)
1117 file
->search
.pos
= file
->size
;
1120 file
->search
.pos
= file
->pos
;
1121 result
= (entry
!= NULL
)
1122 ? parseTagLine (file
, entry
, &file
->err
)
1128 static tagResult
findNextFull (tagFile
*const file
, tagEntry
*const entry
,
1130 int (* isAcceptable
) (tagFile
*const, void *),
1136 result
= tagsNext (file
, entry
);
1137 if (result
== TagSuccess
&& !isAcceptable (file
, data
))
1138 result
= TagFailure
;
1142 result
= findSequentialFull (file
, isAcceptable
, data
);
1143 if (result
== TagSuccess
&& entry
!= NULL
)
1144 result
= parseTagLine (file
, entry
, &file
->err
);
1149 static tagResult
findNext (tagFile
*const file
, tagEntry
*const entry
)
1151 return findNextFull (file
, entry
,
1152 (file
->sortMethod
== TAG_SORTED
&& !file
->search
.ignorecase
) ||
1153 (file
->sortMethod
== TAG_FOLDSORTED
&& file
->search
.ignorecase
),
1154 nameAcceptable
, NULL
);
1157 static tagResult
findPseudoTag (tagFile
*const file
, int rewindBeforeFinding
, tagEntry
*const entry
)
1162 if (!file
->initialized
|| file
->err
)
1164 file
->err
= TagErrnoInvalidArgument
;
1168 if (rewindBeforeFinding
)
1170 if (fseek(file
->fp
, 0L, SEEK_SET
) == -1)
1176 return findNextFull (file
, entry
,
1177 (file
->sortMethod
== TAG_SORTED
|| file
->sortMethod
== TAG_FOLDSORTED
),
1178 doesFilePointPseudoTag
,
1184 * EXTERNAL INTERFACE
1187 extern tagFile
*tagsOpen (const char *const filePath
, tagFileInfo
*const info
)
1189 tagFileInfo infoDummy
;
1190 return initialize (filePath
, info
? info
: &infoDummy
);
1193 extern tagResult
tagsSetSortType (tagFile
*const file
, const tagSortType type
)
1198 if (!file
->initialized
|| file
->err
)
1200 file
->err
= TagErrnoInvalidArgument
;
1208 case TAG_FOLDSORTED
:
1209 file
->sortMethod
= type
;
1212 file
->err
= TagErrnoUnexpectedSortedMethod
;
1217 extern tagResult
tagsFirst (tagFile
*const file
, tagEntry
*const entry
)
1222 if (!file
->initialized
|| file
->err
)
1224 file
->err
= TagErrnoInvalidArgument
;
1228 if (gotoFirstLogicalTag (file
) != TagSuccess
)
1230 return readNext (file
, entry
);
1233 extern tagResult
tagsNext (tagFile
*const file
, tagEntry
*const entry
)
1238 if (!file
->initialized
|| file
->err
)
1240 file
->err
= TagErrnoInvalidArgument
;
1244 return readNext (file
, entry
);
1247 extern const char *tagsField (const tagEntry
*const entry
, const char *const key
)
1249 const char *result
= NULL
;
1251 result
= readFieldValue (entry
, key
);
1255 extern tagResult
tagsFind (tagFile
*const file
, tagEntry
*const entry
,
1256 const char *const name
, const int options
)
1261 if (!file
->initialized
|| file
->err
)
1263 file
->err
= TagErrnoInvalidArgument
;
1267 return find (file
, entry
, name
, options
);
1270 extern tagResult
tagsFindNext (tagFile
*const file
, tagEntry
*const entry
)
1275 if (!file
->initialized
|| file
->err
)
1277 file
->err
= TagErrnoInvalidArgument
;
1281 return findNext (file
, entry
);
1284 extern tagResult
tagsFirstPseudoTag (tagFile
*const file
, tagEntry
*const entry
)
1286 return findPseudoTag (file
, 1, entry
);
1289 extern tagResult
tagsNextPseudoTag (tagFile
*const file
, tagEntry
*const entry
)
1291 return findPseudoTag (file
, 0, entry
);
1294 extern tagResult
tagsClose (tagFile
*const file
)
1296 tagResult result
= TagFailure
;
1297 if (file
!= NULL
&& file
->initialized
)
1300 result
= TagSuccess
;
1305 extern int tagsGetErrno (tagFile
*const file
)
1308 return TagErrnoInvalidArgument
;