2 * Copyright (c) 1996-2002, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for creating tag entries.
13 #include "general.h" /* must always come first */
16 #include <ctype.h> /* to define isspace () */
19 #if defined (HAVE_SYS_TYPES_H)
20 # include <sys/types.h> /* to declare off_t on some hosts */
22 #if defined (HAVE_TYPES_H)
23 # include <types.h> /* to declare off_t on some hosts */
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h> /* to declare close (), ftruncate (), truncate () */
29 /* These header files provide for the functions necessary to do file
40 #include <limits.h> /* to define INT_MAX */
47 #include "nestlevel.h"
48 #include "options_p.h"
54 #include "routines_p.h"
59 #include "subparser_p.h"
71 #if !defined(HAVE_TRUNCATE) && !defined(HAVE_FTRUNCATE) && !defined(HAVE_CHSIZE)
72 # define USE_REPLACEMENT_TRUNCATE
75 /* Hack for ridiculous practice of Microsoft Visual C++.
77 #if defined (WIN32) && defined (_MSC_VER)
78 # define chsize _chsize
81 # define O_RDWR _O_RDWR
85 /* Maintains the state of the tag file.
87 typedef struct eTagFile
{
91 struct sNumTags
{ unsigned long added
, prev
; } numTags
;
92 struct sMax
{ size_t line
, tag
; } max
;
96 unsigned int corkFlags
;
99 bool patternCacheValid
;
102 typedef struct sTagEntryInfoX
{
105 struct rb_root symtab
;
106 struct rb_node symnode
;
113 static tagFile TagFile
= {
114 NULL
, /* tag file name */
115 NULL
, /* tag file directory (absolute) */
116 NULL
, /* file pointer */
117 { 0, 0 }, /* numTags */
122 .patternCacheValid
= false,
125 static bool TagsToStdout
= false;
128 * FUNCTION PROTOTYPES
130 #ifdef NEED_PROTO_TRUNCATE
131 extern int truncate (const char *path
, off_t length
);
134 #ifdef NEED_PROTO_FTRUNCATE
135 extern int ftruncate (int fd
, off_t length
);
139 * FUNCTION DEFINITIONS
142 extern void freeTagFileResources (void)
144 if (TagFile
.directory
!= NULL
)
145 eFree (TagFile
.directory
);
146 vStringDelete (TagFile
.vLine
);
149 extern const char *tagFileName (void)
158 extern void abort_if_ferror(MIO
*const mio
)
160 if (mio
!= NULL
&& mio_error (mio
))
161 error (FATAL
| PERROR
, "cannot write tag file");
164 static void rememberMaxLengths (const size_t nameLength
, const size_t lineLength
)
166 if (nameLength
> TagFile
.max
.tag
)
167 TagFile
.max
.tag
= nameLength
;
169 if (lineLength
> TagFile
.max
.line
)
170 TagFile
.max
.line
= lineLength
;
173 static void addCommonPseudoTags (void)
175 for (int i
= 0; i
< PTAG_COUNT
; i
++)
177 if (isPtagCommonInParsers (i
))
178 makePtagIfEnabled (i
, LANG_IGNORE
, &Option
);
182 extern void makeFileTag (const char *const fileName
)
186 if (!isXtagEnabled(XTAG_FILE_NAMES
))
189 initTagEntry (&tag
, baseFilename (fileName
), KIND_FILE_INDEX
);
191 tag
.isFileEntry
= true;
192 tag
.lineNumberEntry
= true;
193 markTagExtraBit (&tag
, XTAG_FILE_NAMES
);
196 if (isFieldEnabled (FIELD_END_LINE
))
198 /* isFieldEnabled is called again in the rendering
199 stage. However, it is called here for avoiding
200 unnecessary read line loop. */
201 while (readLineFromInputFile () != NULL
)
203 tag
.extensionFields
.endLine
= getInputLineNumber ();
206 if (isFieldEnabled (FIELD_EPOCH
))
207 tag
.extensionFields
.epoch
= getInputFileMtime ();
212 static void updateSortedFlag (
213 const char *const line
, MIO
*const mio
, MIOPos startOfLine
)
215 const char *const tab
= strchr (line
, '\t');
219 const long boolOffset
= tab
- line
+ 1; /* where it should be */
221 if (line
[boolOffset
] == '0' || line
[boolOffset
] == '1')
225 if (mio_getpos (mio
, &nextLine
) == -1 || mio_setpos (mio
, &startOfLine
) == -1)
226 error (WARNING
, "Failed to update 'sorted' pseudo-tag");
234 while (c
!= '\t' && c
!= '\n');
235 mio_getpos (mio
, &flagLocation
);
237 if (c
== '\t' && (d
== '0' || d
== '1') &&
238 d
!= (int) Option
.sorted
)
240 mio_setpos (mio
, &flagLocation
);
241 mio_putc (mio
, Option
.sorted
== SO_FOLDSORTED
? '2' :
242 (Option
.sorted
== SO_SORTED
? '1' : '0'));
244 mio_setpos (mio
, &nextLine
);
250 /* Look through all line beginning with "!_TAG_FILE", and update those which
253 static long unsigned int updatePseudoTags (MIO
*const mio
)
255 enum { maxEntryLength
= 20 };
256 char entry
[maxEntryLength
+ 1];
257 unsigned long linesRead
= 0;
262 sprintf (entry
, "%sTAG_FILE", PSEUDO_TAG_PREFIX
);
263 entryLength
= strlen (entry
);
264 Assert (entryLength
< maxEntryLength
);
266 mio_getpos (mio
, &startOfLine
);
267 line
= readLineRaw (TagFile
.vLine
, mio
);
268 while (line
!= NULL
&& line
[0] == entry
[0])
271 if (strncmp (line
, entry
, entryLength
) == 0)
273 char tab
, classType
[16];
275 if (sscanf (line
+ entryLength
, "%15s%c", classType
, &tab
) == 2 &&
278 if (strcmp (classType
, "_SORTED") == 0)
279 updateSortedFlag (line
, mio
, startOfLine
);
281 mio_getpos (mio
, &startOfLine
);
283 line
= readLineRaw (TagFile
.vLine
, mio
);
285 while (line
!= NULL
) /* skip to end of file */
288 line
= readLineRaw (TagFile
.vLine
, mio
);
294 * Tag file management
297 static bool isValidTagAddress (const char *const excmd
)
299 bool isValid
= false;
301 if (strchr ("/?", excmd
[0]) != NULL
)
305 char *address
= xMalloc (strlen (excmd
) + 1, char);
306 if (sscanf (excmd
, "%[^;\n]", address
) == 1 &&
307 strspn (address
,"0123456789") == strlen (address
))
314 static bool isCtagsLine (const char *const line
)
316 enum fieldList
{ TAG
, TAB1
, SRC_FILE
, TAB2
, EXCMD
, NUM_FIELDS
};
317 bool ok
= false; /* we assume not unless confirmed */
318 const size_t fieldLength
= strlen (line
) + 1;
319 char *const fields
= xMalloc (NUM_FIELDS
* fieldLength
, char);
322 error (FATAL
, "Cannot analyze tag file");
325 #define field(x) (fields + ((size_t) (x) * fieldLength))
327 const int numFields
= sscanf (
328 line
, "%[^\t]%[\t]%[^\t]%[\t]%[^\r\n]",
329 field (TAG
), field (TAB1
), field (SRC_FILE
),
330 field (TAB2
), field (EXCMD
));
332 /* There must be exactly five fields: two tab fields containing
333 * exactly one tab each, the tag must not begin with "#", and the
334 * file name should not end with ";", and the excmd must be
337 * These conditions will reject tag-looking lines like:
339 * #define LABEL <C-comment>
341 if (numFields
== NUM_FIELDS
&&
342 strlen (field (TAB1
)) == 1 &&
343 strlen (field (TAB2
)) == 1 &&
344 field (TAG
) [0] != '#' &&
345 field (SRC_FILE
) [strlen (field (SRC_FILE
)) - 1] != ';' &&
346 isValidTagAddress (field (EXCMD
)))
354 static bool isEtagsLine (const char *const line
)
357 if (line
[0] == '\f')
358 result
= (bool) (line
[1] == '\n' || line
[1] == '\r');
362 static bool isTagFile (const char *const filename
)
364 bool ok
= false; /* we assume not unless confirmed */
365 MIO
*const mio
= mio_new_file (filename
, "rb");
367 if (mio
== NULL
&& errno
== ENOENT
)
369 else if (mio
!= NULL
)
371 const char *line
= readLineRaw (TagFile
.vLine
, mio
);
376 ok
= (bool) (isCtagsLine (line
) || isEtagsLine (line
));
382 extern void openTagFile (void)
384 setDefaultTagFileName ();
385 TagsToStdout
= isDestinationStdout ();
387 if (TagFile
.vLine
== NULL
)
388 TagFile
.vLine
= vStringNew ();
390 /* Open the tags file.
394 if (Option
.interactive
== INTERACTIVE_SANDBOX
)
396 TagFile
.mio
= mio_new_memory (NULL
, 0, eRealloc
, eFreeNoNullCheck
);
400 TagFile
.mio
= tempFile ("w+", &TagFile
.name
);
401 if (isXtagEnabled (XTAG_PSEUDO_TAGS
))
402 addCommonPseudoTags ();
408 TagFile
.name
= eStrdup (Option
.tagFileName
);
409 fileExists
= doesFileExist (TagFile
.name
);
410 if (fileExists
&& ! isTagFile (TagFile
.name
))
412 "\"%s\" doesn't look like a tag file; I refuse to overwrite it.",
417 if (Option
.append
&& fileExists
)
418 TagFile
.mio
= mio_new_file (TagFile
.name
, "a+b");
420 TagFile
.mio
= mio_new_file (TagFile
.name
, "w+b");
424 if (Option
.append
&& fileExists
)
426 TagFile
.mio
= mio_new_file (TagFile
.name
, "r+");
427 if (TagFile
.mio
!= NULL
)
429 TagFile
.numTags
.prev
= updatePseudoTags (TagFile
.mio
);
430 mio_unref (TagFile
.mio
);
431 TagFile
.mio
= mio_new_file (TagFile
.name
, "a+");
436 TagFile
.mio
= mio_new_file (TagFile
.name
, "w");
437 if (TagFile
.mio
!= NULL
&& isXtagEnabled (XTAG_PSEUDO_TAGS
))
438 addCommonPseudoTags ();
441 if (TagFile
.mio
== NULL
)
442 error (FATAL
| PERROR
, "cannot open tag file");
445 if (TagFile
.directory
== NULL
)
448 TagFile
.directory
= eStrdup (CurrentDirectory
);
450 TagFile
.directory
= absoluteDirname (TagFile
.name
);
454 #ifdef USE_REPLACEMENT_TRUNCATE
456 static void copyBytes (MIO
* const fromMio
, MIO
* const toMio
, const long size
)
458 enum { BufferSize
= 1000 };
459 long toRead
, numRead
;
460 char* buffer
= xMalloc (BufferSize
, char);
461 long remaining
= size
;
464 toRead
= (0 < remaining
&& remaining
< BufferSize
) ?
465 remaining
: (long) BufferSize
;
466 numRead
= mio_read (fromMio
, buffer
, (size_t) 1, (size_t) toRead
);
467 if (mio_write (toMio
, buffer
, (size_t)1, (size_t)numRead
) < (size_t)numRead
)
468 error (FATAL
| PERROR
, "cannot complete write");
470 remaining
-= numRead
;
471 } while (numRead
== toRead
&& remaining
!= 0);
475 static void copyFile (const char *const from
, const char *const to
, const long size
)
477 MIO
* const fromMio
= mio_new_file (from
, "rb");
479 error (FATAL
| PERROR
, "cannot open file to copy");
482 MIO
* const toMio
= mio_new_file (to
, "wb");
484 error (FATAL
| PERROR
, "cannot open copy destination");
487 copyBytes (fromMio
, toMio
, size
);
494 /* Replacement for missing library function.
496 static int replacementTruncate (const char *const name
, const long size
)
498 #define WHOLE_FILE -1L
499 char *tempName
= NULL
;
500 MIO
*mio
= tempFile ("w", &tempName
);
502 copyFile (name
, tempName
, size
);
503 copyFile (tempName
, name
, WHOLE_FILE
);
512 #ifndef EXTERNAL_SORT
513 static void internalSortTagFile (void)
517 /* Open/Prepare the tag file and place its lines into allocated buffers.
522 mio_seek (mio
, 0, SEEK_SET
);
526 mio
= mio_new_file (tagFileName (), "r");
528 failedSort (mio
, NULL
);
531 internalSortTags (TagsToStdout
,
533 TagFile
.numTags
.added
+ TagFile
.numTags
.prev
);
540 static void sortTagFile (void)
542 if (TagFile
.numTags
.added
> 0L)
544 if (Option
.sorted
!= SO_UNSORTED
)
546 verbose ("sorting tag file\n");
548 externalSortTags (TagsToStdout
, TagFile
.mio
);
550 internalSortTagFile ();
553 else if (TagsToStdout
)
554 catFile (TagFile
.mio
);
558 static void resizeTagFile (const long newSize
)
564 mio_try_resize (TagFile
.mio
, newSize
);
568 #ifdef USE_REPLACEMENT_TRUNCATE
569 result
= replacementTruncate (TagFile
.name
, newSize
);
571 # ifdef HAVE_TRUNCATE
572 result
= truncate (TagFile
.name
, (off_t
) newSize
);
574 const int fd
= open (TagFile
.name
, O_RDWR
);
580 # ifdef HAVE_FTRUNCATE
581 result
= ftruncate (fd
, (off_t
) newSize
);
584 result
= chsize (fd
, newSize
);
592 fprintf (stderr
, "Cannot shorten tag file: errno = %d\n", errno
);
595 static void writeEtagsIncludes (MIO
*const mio
)
597 if (Option
.etagsInclude
)
600 for (i
= 0 ; i
< stringListCount (Option
.etagsInclude
) ; ++i
)
602 vString
*item
= stringListItem (Option
.etagsInclude
, i
);
603 mio_printf (mio
, "\f\n%s,include\n", vStringValue (item
));
608 extern void closeTagFile (const bool resize
)
610 long desiredSize
, size
;
613 writeEtagsIncludes (TagFile
.mio
);
614 mio_flush (TagFile
.mio
);
616 abort_if_ferror (TagFile
.mio
);
617 desiredSize
= mio_tell (TagFile
.mio
);
618 mio_seek (TagFile
.mio
, 0L, SEEK_END
);
619 size
= mio_tell (TagFile
.mio
);
621 /* The tag file should be closed before resizing. */
622 if (mio_unref (TagFile
.mio
) != 0)
623 error (FATAL
| PERROR
, "cannot close tag file");
625 if (resize
&& desiredSize
< size
)
628 debugPrintf (DEBUG_STATUS
, "shrinking %s from %ld to %ld bytes\n",
629 TagFile
.name
? TagFile
.name
: "<mio>", size
, desiredSize
); )
630 resizeTagFile (desiredSize
);
635 if (mio_unref (TagFile
.mio
) != 0)
636 error (FATAL
| PERROR
, "cannot close tag file");
638 remove (TagFile
.name
); /* remove temporary file */
643 eFree (TagFile
.name
);
648 * Tag entry management
651 /* This function copies the current line out to a specified file. It has no
652 * effect on the fileGetc () function. During copying, any '\' characters
653 * are doubled and a leading '^' or trailing '$' is also quoted. End of line
654 * characters (line feed or carriage return) are dropped.
656 static size_t appendInputLine (int putc_func (char , void *), const char *const line
,
657 unsigned int patternLengthLimit
,
658 void * data
, bool *omitted
)
664 /* Write everything up to, but not including, a line end character.
667 for (p
= line
; *p
!= '\0' ; ++p
)
669 const int next
= *(p
+ 1);
672 if (c
== CRETURN
|| c
== NEWLINE
)
675 if (patternLengthLimit
!= 0 && length
>= patternLengthLimit
&&
676 /* Do not cut inside a multi-byte UTF-8 character, but safe-guard it not to
677 * allow more than one extra valid UTF-8 character in case it's not actually
678 * UTF-8. To do that, limit to an extra 3 UTF-8 sub-bytes (0b10xxxxxx). */
679 ((((unsigned char) c
) & 0xc0) != 0x80 || ++extraLength
> 3))
684 /* If character is '\', or a terminal '$', then quote it.
686 if (c
== BACKSLASH
|| c
== (Option
.backward
? '?' : '/') ||
687 (c
== '$' && (next
== NEWLINE
|| next
== CRETURN
)))
689 putc_func (BACKSLASH
, data
);
699 static int vstring_putc (char c
, void *data
)
706 static int vstring_puts (const char* s
, void *data
)
709 size_t len
= vStringLength (str
);
710 vStringCatS (str
, s
);
711 return (int) (vStringLength (str
) - len
);
715 static bool isPosSet(MIOPos pos
)
717 char * p
= (char *)&pos
;
721 for (i
= 0; i
< sizeof(pos
); i
++)
727 extern char *readLineFromBypassForTag (vString
*const vLine
, const tagEntryInfo
*const tag
,
728 long *const pSeekValue
)
730 Assert (isPosSet (tag
->filePosition
) || (tag
->pattern
== NULL
));
731 return readLineFromBypass (vLine
, tag
->filePosition
, pSeekValue
);
734 /* Truncates the text line containing the tag at the character following the
735 * tag, providing a character which designates the end of the tag.
736 * Returns the length of the truncated line (or 0 if it doesn't truncate).
738 extern size_t truncateTagLineAfterTag (
739 char *const line
, const char *const token
, const bool discardNewline
)
742 char *p
= strstr (line
, token
);
747 if (*p
!= '\0' && ! (*p
== '\n' && discardNewline
))
748 ++p
; /* skip past character terminating character */
756 static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo
* inner_scope
)
759 int kindIndex
= KIND_GHOST_INDEX
;
761 const tagEntryInfo
*scope
= inner_scope
;
762 const tagEntryInfo
*root_scope
= NULL
;
763 stringList
*queue
= stringListNew ();
771 if (!scope
->placeholder
)
773 if (kindIndex
!= KIND_GHOST_INDEX
)
775 sep
= scopeSeparatorFor (lang
, kindIndex
, scope
->kindIndex
);
776 v
= vStringNewInit (sep
);
777 stringListAdd (queue
, v
);
779 /* TODO: scope field of SCOPE can be used for optimization. */
780 v
= vStringNewInit (scope
->name
);
781 stringListAdd (queue
, v
);
782 kindIndex
= scope
->kindIndex
;
783 lang
= scope
->langType
;
786 scope
= getEntryInCorkQueue (scope
->extensionFields
.scopeIndex
);
790 sep
= root_scope
? scopeSeparatorFor (root_scope
->langType
, root_scope
->kindIndex
, KIND_GHOST_INDEX
): NULL
;
794 while ((c
= stringListCount (queue
)) > 0)
796 v
= stringListLast (queue
);
799 stringListRemoveLast (queue
);
801 stringListDelete (queue
);
803 return vStringDeleteUnwrap (n
);
806 extern void getTagScopeInformation (tagEntryInfo
*const tag
,
807 const char **kind
, const char **name
)
814 const tagEntryInfo
* scope
= getEntryInCorkQueue (tag
->extensionFields
.scopeIndex
);
815 if (tag
->extensionFields
.scopeKindIndex
== KIND_GHOST_INDEX
816 && tag
->extensionFields
.scopeName
== NULL
818 && ptrArrayCount (TagFile
.corkQueue
) > 0)
820 char *full_qualified_scope_name
= getFullQualifiedScopeNameFromCorkQueue(scope
);
821 Assert (full_qualified_scope_name
);
823 /* Make the information reusable to generate full qualified entry, and xformat output*/
824 tag
->extensionFields
.scopeLangType
= scope
->langType
;
825 tag
->extensionFields
.scopeKindIndex
= scope
->kindIndex
;
826 tag
->extensionFields
.scopeName
= full_qualified_scope_name
;
829 if (tag
->extensionFields
.scopeKindIndex
!= KIND_GHOST_INDEX
&&
830 tag
->extensionFields
.scopeName
!= NULL
)
834 langType lang
= (tag
->extensionFields
.scopeLangType
== LANG_AUTO
)
836 : tag
->extensionFields
.scopeLangType
;
837 kindDefinition
*kdef
= getLanguageKind (lang
,
838 tag
->extensionFields
.scopeKindIndex
);
842 *name
= tag
->extensionFields
.scopeName
;
847 static int makePatternStringCommon (const tagEntryInfo
*const tag
,
848 int (* putc_func
) (char , void *),
849 int (* puts_func
) (const char* , void *),
856 const char *terminator
;
860 bool making_cache
= false;
861 int (* puts_o_func
)(const char* , void *);
864 static vString
*cached_pattern
;
865 static MIOPos cached_location
;
866 if (TagFile
.patternCacheValid
867 && (! tag
->truncateLineAfterTag
)
868 && (memcmp (&tag
->filePosition
, &cached_location
, sizeof(MIOPos
)) == 0))
869 return puts_func (vStringValue (cached_pattern
), output
);
871 line
= readLineFromBypassForTag (TagFile
.vLine
, tag
, NULL
);
874 /* This can be occurs if the size of input file is zero, and
875 an empty regex pattern (//) matches to the input. */
880 line_len
= vStringLength (TagFile
.vLine
);
882 if (tag
->truncateLineAfterTag
)
886 truncted_len
= truncateTagLineAfterTag (line
, tag
->name
, false);
887 if (truncted_len
> 0)
888 line_len
= truncted_len
;
891 searchChar
= Option
.backward
? '?' : '/';
892 terminator
= (line_len
> 0 && (line
[line_len
- 1] == '\n')) ? "$": "";
894 if (!tag
->truncateLineAfterTag
)
897 cached_pattern
= vStringNewOrClearWithAutoRelease (cached_pattern
);
899 puts_o_func
= puts_func
;
901 putc_func
= vstring_putc
;
902 puts_func
= vstring_puts
;
903 output
= cached_pattern
;
906 length
+= putc_func(searchChar
, output
);
907 if ((tag
->boundaryInfo
& BOUNDARY_START
) == 0)
908 length
+= putc_func('^', output
);
909 length
+= appendInputLine (putc_func
, line
, Option
.patternLengthLimit
,
911 length
+= puts_func (omitted
? "": terminator
, output
);
912 length
+= putc_func (searchChar
, output
);
916 puts_o_func (vStringValue (cached_pattern
), o_output
);
917 cached_location
= tag
->filePosition
;
918 TagFile
.patternCacheValid
= true;
924 extern char* makePatternString (const tagEntryInfo
*const tag
)
926 vString
* pattern
= vStringNew ();
927 makePatternStringCommon (tag
, vstring_putc
, vstring_puts
, pattern
);
928 return vStringDeleteUnwrap (pattern
);
931 static tagField
* tagFieldNew(fieldType ftype
, const char *value
, bool valueOwner
)
933 tagField
*f
= xMalloc (1, tagField
);
937 f
->valueOwner
= valueOwner
;
941 static void tagFieldDelete (tagField
* f
)
944 eFree((void *)f
->value
);
948 static void attachParserFieldGeneric (tagEntryInfo
*const tag
, fieldType ftype
, const char * value
,
951 if (tag
->usedParserFields
< PRE_ALLOCATED_PARSER_FIELDS
)
953 tag
->parserFields
[tag
->usedParserFields
].ftype
= ftype
;
954 tag
->parserFields
[tag
->usedParserFields
].value
= value
;
955 tag
->parserFields
[tag
->usedParserFields
].valueOwner
= valueOwner
;
956 tag
->usedParserFields
++;
958 else if (tag
->parserFieldsDynamic
== NULL
)
960 tag
->parserFieldsDynamic
= ptrArrayNew((ptrArrayDeleteFunc
)tagFieldDelete
);
961 PARSER_TRASH_BOX(tag
->parserFieldsDynamic
, ptrArrayDelete
);
962 attachParserFieldGeneric (tag
, ftype
, value
, valueOwner
);
966 tagField
*f
= tagFieldNew (ftype
, value
, valueOwner
);
967 ptrArrayAdd(tag
->parserFieldsDynamic
, f
);
968 tag
->usedParserFields
++;
972 extern void attachParserField (tagEntryInfo
*const tag
, bool inCorkQueue
, fieldType ftype
, const char * value
)
974 Assert (tag
!= NULL
);
981 bool dynfields_allocated
= tag
->parserFieldsDynamic
? true: false;
982 attachParserFieldGeneric (tag
, ftype
, v
, true);
983 if (!dynfields_allocated
&& tag
->parserFieldsDynamic
)
984 PARSER_TRASH_BOX_TAKE_BACK(tag
->parserFieldsDynamic
);
987 attachParserFieldGeneric (tag
, ftype
, value
, false);
990 extern void attachParserFieldToCorkEntry (int index
,
994 tagEntryInfo
* tag
= getEntryInCorkQueue (index
);
996 attachParserField (tag
, true, ftype
, value
);
999 extern const tagField
* getParserFieldForIndex (const tagEntryInfo
* tag
, int index
)
1002 || tag
->usedParserFields
<= ((unsigned int)index
) )
1004 else if (index
< PRE_ALLOCATED_PARSER_FIELDS
)
1005 return tag
->parserFields
+ index
;
1008 unsigned int n
= index
- PRE_ALLOCATED_PARSER_FIELDS
;
1009 return ptrArrayItem(tag
->parserFieldsDynamic
, n
);
1013 extern const char* getParserFieldValueForType (tagEntryInfo
*const tag
, fieldType ftype
)
1015 for (int i
= 0; i
< tag
->usedParserFields
; i
++)
1017 const tagField
*f
= getParserFieldForIndex (tag
, i
);
1018 if (f
&& f
->ftype
== ftype
)
1024 static void copyParserFields (const tagEntryInfo
*const tag
, tagEntryInfo
* slot
)
1029 for (i
= 0; i
< tag
->usedParserFields
; i
++)
1031 const tagField
*f
= getParserFieldForIndex (tag
, i
);
1036 value
= eStrdup (value
);
1038 attachParserFieldGeneric (slot
,
1046 static tagEntryInfo
*newNilTagEntry (unsigned int corkFlags
)
1048 tagEntryInfoX
*x
= xCalloc (1, tagEntryInfoX
);
1049 x
->corkIndex
= CORK_NIL
;
1050 x
->symtab
= RB_ROOT
;
1051 x
->slot
.kindIndex
= KIND_FILE_INDEX
;
1055 static tagEntryInfoX
*copyTagEntry (const tagEntryInfo
*const tag
,
1056 unsigned int corkFlags
)
1058 tagEntryInfoX
*x
= xMalloc (1, tagEntryInfoX
);
1059 x
->symtab
= RB_ROOT
;
1060 x
->corkIndex
= CORK_NIL
;
1061 tagEntryInfo
*slot
= (tagEntryInfo
*)x
;
1066 slot
->pattern
= eStrdup (slot
->pattern
);
1068 slot
->inputFileName
= eStrdup (slot
->inputFileName
);
1069 slot
->name
= eStrdup (slot
->name
);
1070 if (slot
->extensionFields
.access
)
1071 slot
->extensionFields
.access
= eStrdup (slot
->extensionFields
.access
);
1072 if (slot
->extensionFields
.implementation
)
1073 slot
->extensionFields
.implementation
= eStrdup (slot
->extensionFields
.implementation
);
1074 if (slot
->extensionFields
.inheritance
)
1075 slot
->extensionFields
.inheritance
= eStrdup (slot
->extensionFields
.inheritance
);
1076 if (slot
->extensionFields
.scopeName
)
1077 slot
->extensionFields
.scopeName
= eStrdup (slot
->extensionFields
.scopeName
);
1078 if (slot
->extensionFields
.signature
)
1079 slot
->extensionFields
.signature
= eStrdup (slot
->extensionFields
.signature
);
1080 if (slot
->extensionFields
.typeRef
[0])
1081 slot
->extensionFields
.typeRef
[0] = eStrdup (slot
->extensionFields
.typeRef
[0]);
1082 if (slot
->extensionFields
.typeRef
[1])
1083 slot
->extensionFields
.typeRef
[1] = eStrdup (slot
->extensionFields
.typeRef
[1]);
1085 if (slot
->extensionFields
.xpath
)
1086 slot
->extensionFields
.xpath
= eStrdup (slot
->extensionFields
.xpath
);
1089 if (slot
->extraDynamic
)
1091 int n
= countXtags () - XTAG_COUNT
;
1092 slot
->extraDynamic
= xCalloc ((n
/ 8) + 1, uint8_t);
1093 memcpy (slot
->extraDynamic
, tag
->extraDynamic
, (n
/ 8) + 1);
1096 if (slot
->sourceFileName
)
1097 slot
->sourceFileName
= eStrdup (slot
->sourceFileName
);
1100 slot
->usedParserFields
= 0;
1101 slot
->parserFieldsDynamic
= NULL
;
1102 copyParserFields (tag
, slot
);
1103 if (slot
->parserFieldsDynamic
)
1104 PARSER_TRASH_BOX_TAKE_BACK(slot
->parserFieldsDynamic
);
1109 static void clearParserFields (tagEntryInfo
*const tag
)
1114 if ( tag
->usedParserFields
< PRE_ALLOCATED_PARSER_FIELDS
)
1115 n
= tag
->usedParserFields
;
1117 n
= PRE_ALLOCATED_PARSER_FIELDS
;
1119 for (i
= 0; i
< n
; i
++)
1121 value
= tag
->parserFields
[i
].value
;
1122 if (value
&& tag
->parserFields
[i
].valueOwner
)
1123 eFree ((char *)value
);
1124 tag
->parserFields
[i
].value
= NULL
;
1125 tag
->parserFields
[i
].ftype
= FIELD_UNKNOWN
;
1127 if (tag
->parserFieldsDynamic
)
1129 ptrArrayDelete (tag
->parserFieldsDynamic
);
1130 tag
->parserFieldsDynamic
= NULL
;
1134 static void deleteTagEnry (void *data
)
1136 tagEntryInfo
*slot
= data
;
1138 if (slot
->kindIndex
== KIND_FILE_INDEX
)
1142 eFree ((char *)slot
->pattern
);
1143 eFree ((char *)slot
->inputFileName
);
1144 eFree ((char *)slot
->name
);
1146 if (slot
->extensionFields
.access
)
1147 eFree ((char *)slot
->extensionFields
.access
);
1148 if (slot
->extensionFields
.implementation
)
1149 eFree ((char *)slot
->extensionFields
.implementation
);
1150 if (slot
->extensionFields
.inheritance
)
1151 eFree ((char *)slot
->extensionFields
.inheritance
);
1152 if (slot
->extensionFields
.scopeName
)
1153 eFree ((char *)slot
->extensionFields
.scopeName
);
1154 if (slot
->extensionFields
.signature
)
1155 eFree ((char *)slot
->extensionFields
.signature
);
1156 if (slot
->extensionFields
.typeRef
[0])
1157 eFree ((char *)slot
->extensionFields
.typeRef
[0]);
1158 if (slot
->extensionFields
.typeRef
[1])
1159 eFree ((char *)slot
->extensionFields
.typeRef
[1]);
1161 if (slot
->extensionFields
.xpath
)
1162 eFree ((char *)slot
->extensionFields
.xpath
);
1165 if (slot
->extraDynamic
)
1166 eFree (slot
->extraDynamic
);
1168 if (slot
->sourceFileName
)
1169 eFree ((char *)slot
->sourceFileName
);
1171 clearParserFields (slot
);
1177 static void corkSymtabPut (tagEntryInfoX
*scope
, const char* name
, tagEntryInfoX
*item
)
1179 struct rb_root
*root
= &scope
->symtab
;
1181 struct rb_node
**new = &(root
->rb_node
), *parent
= NULL
;
1185 tagEntryInfoX
*this = container_of(*new, tagEntryInfoX
, symnode
);
1186 int result
= strcmp(item
->slot
.name
, this->slot
.name
);
1191 new = &((*new)->rb_left
);
1192 else if (result
> 0)
1193 new = &((*new)->rb_right
);
1196 unsigned long lthis
= this->slot
.lineNumber
;
1197 unsigned long litem
= item
->slot
.lineNumber
;
1199 /* Comparing lineNumber */
1201 new = &((*new)->rb_left
);
1202 else if (litem
> lthis
)
1203 new = &((*new)->rb_right
);
1206 /* Comparing memory address */
1208 new = &((*new)->rb_left
);
1209 else if (item
> this)
1210 new = &((*new)->rb_right
);
1213 AssertNotReached(); /* registering the same object twice. */
1220 verbose ("symtbl[:=] %s<-%s/%p (line: %lu)\n",
1221 *new? container_of(*new, tagEntryInfoX
, symnode
)->slot
.name
: "*root*",
1222 item
->slot
.name
, &item
->slot
, item
->slot
.lineNumber
);
1223 /* Add new node and rebalance tree. */
1224 rb_link_node(&item
->symnode
, parent
, new);
1225 rb_insert_color(&item
->symnode
, root
);
1228 extern bool foreachEntriesInScope (int corkIndex
,
1230 entryForeachFunc func
,
1233 tagEntryInfoX
*x
= ptrArrayItem (TagFile
.corkQueue
, corkIndex
);
1235 struct rb_root
*root
= &x
->symtab
;
1236 tagEntryInfoX
*rep
= NULL
;
1238 /* More than one tag can have a same name.
1239 * Visit them from the last.
1241 * 1. find one of them as the representative,
1242 * 2. find the last one of them from the representative with rb_next,
1243 * 3. call FUNC iteratively from the last to the first.
1247 struct rb_node
*node
= root
->rb_node
;
1250 tagEntryInfoX
*entry
= container_of(node
, tagEntryInfoX
, symnode
);
1253 result
= strcmp(name
, entry
->slot
.name
);
1256 node
= node
->rb_left
;
1257 else if (result
> 0)
1258 node
= node
->rb_right
;
1268 verbose("symtbl[<>] %s->%p\n", name
, &rep
->slot
);
1271 struct rb_node
*last
;
1275 struct rb_node
*tmp
= &rep
->symnode
;
1278 while ((tmp
= rb_next (tmp
)))
1280 tagEntryInfoX
*entry
= container_of(tmp
, tagEntryInfoX
, symnode
);
1281 if (strcmp(name
, entry
->slot
.name
) == 0)
1283 verbose ("symtbl[ >] %s->%p\n", name
, &container_of(tmp
, tagEntryInfoX
, symnode
)->slot
);
1292 last
= rb_last(root
);
1293 verbose ("last for %d<%p>: %p\n", corkIndex
, root
, last
);
1298 verbose ("symtbl[>V] %s->%p\n", name
? name
: "(null)", NULL
);
1299 return true; /* Nothing here in this node. */
1302 verbose ("symtbl[>|] %s->%p\n", name
, &container_of(last
, tagEntryInfoX
, symnode
)->slot
);
1304 struct rb_node
*cursor
= last
;
1305 bool revisited_rep
= false;
1308 tagEntryInfoX
*entry
= container_of(cursor
, tagEntryInfoX
, symnode
);
1309 if (!revisited_rep
|| !name
|| strcmp(name
, entry
->slot
.name
))
1311 verbose ("symtbl[< ] %s->%p\n", name
, &entry
->slot
);
1312 if (!func (entry
->corkIndex
, &entry
->slot
, data
))
1314 if (cursor
== &rep
->symnode
)
1315 revisited_rep
= true;
1320 while ((cursor
= rb_prev(cursor
)));
1325 static bool findName (int corkIndex
, tagEntryInfo
*entry
, void *data
)
1333 int anyEntryInScope (int corkIndex
, const char *name
)
1335 int index
= CORK_NIL
;
1337 if (foreachEntriesInScope (corkIndex
, name
, findName
, &index
) == false)
1343 struct anyKindsEntryInScopeData
{
1349 static bool findNameOfKinds (int corkIndex
, tagEntryInfo
*entry
, void *data
)
1351 struct anyKindsEntryInScopeData
* kdata
= data
;
1353 for (int i
= 0; i
< kdata
->count
; i
++)
1355 int k
= kdata
->kinds
[i
];
1356 if (entry
->kindIndex
== k
)
1358 kdata
->index
= corkIndex
;
1365 int anyKindEntryInScope (int corkIndex
,
1366 const char *name
, int kind
)
1368 return anyKindsEntryInScope (corkIndex
, name
, &kind
, 1);
1371 int anyKindsEntryInScope (int corkIndex
,
1373 const int *kinds
, int count
)
1375 struct anyKindsEntryInScopeData data
= {
1381 if (foreachEntriesInScope (corkIndex
, name
, findNameOfKinds
, &data
) == false)
1387 int anyKindsEntryInScopeRecursive (int corkIndex
,
1389 const int *kinds
, int count
)
1391 struct anyKindsEntryInScopeData data
= {
1400 if (foreachEntriesInScope (corkIndex
, name
, findNameOfKinds
, &data
) == false)
1403 if (corkIndex
== CORK_NIL
)
1406 e
= getEntryInCorkQueue (corkIndex
);
1409 corkIndex
= e
->extensionFields
.scopeIndex
;
1416 extern void registerEntry (int corkIndex
)
1418 Assert (TagFile
.corkFlags
& CORK_SYMTAB
);
1419 Assert (corkIndex
!= CORK_NIL
);
1421 tagEntryInfoX
*e
= ptrArrayItem (TagFile
.corkQueue
, corkIndex
);
1423 tagEntryInfoX
*scope
= ptrArrayItem (TagFile
.corkQueue
, e
->slot
.extensionFields
.scopeIndex
);
1424 corkSymtabPut (scope
, e
->slot
.name
, e
);
1428 static int queueTagEntry(const tagEntryInfo
*const tag
)
1433 tagEntryInfoX
* entry
= copyTagEntry (tag
,
1436 if (ptrArrayCount (TagFile
.corkQueue
) == (size_t)INT_MAX
)
1442 "The tag entry queue overflows; drop the tag entry at %lu in %s",
1444 tag
->inputFileName
);
1450 corkIndex
= (int)ptrArrayAdd (TagFile
.corkQueue
, entry
);
1451 entry
->corkIndex
= corkIndex
;
1452 entry
->slot
.inCorkQueue
= 1;
1457 extern void setupWriter (void *writerClientData
)
1459 writerSetup (TagFile
.mio
, writerClientData
);
1462 extern bool teardownWriter (const char *filename
)
1464 return writerTeardown (TagFile
.mio
, filename
);
1467 static bool isTagWritable(const tagEntryInfo
*const tag
)
1469 if (tag
->placeholder
)
1472 if (! isLanguageKindEnabled(tag
->langType
, tag
->kindIndex
))
1475 if (tag
->extensionFields
.roleBits
)
1477 size_t available_roles
;
1479 if (!isXtagEnabled (XTAG_REFERENCE_TAGS
))
1482 available_roles
= countLanguageRoles(tag
->langType
,
1484 if (tag
->extensionFields
.roleBits
>=
1485 (makeRoleBit(available_roles
)))
1488 /* TODO: optimization
1489 A Bitmasks representing all enabled roles can be calculated at the
1490 end of initializing the parser. Calculating each time when checking
1491 a tag entry is not needed. */
1492 for (unsigned int roleIndex
= 0; roleIndex
< available_roles
; roleIndex
++)
1494 if (isRoleAssigned(tag
, roleIndex
))
1496 if (isLanguageRoleEnabled (tag
->langType
, tag
->kindIndex
,
1504 else if (isLanguageKindRefOnly(tag
->langType
, tag
->kindIndex
))
1506 error (WARNING
, "definition tag for refonly kind(%s) is made: %s",
1507 getLanguageKind(tag
->langType
, tag
->kindIndex
)->name
,
1509 /* This one is not so critical. */
1512 if (!isXtagEnabled(XTAG_ANONYMOUS
)
1513 && isTagExtraBitMarked(tag
, XTAG_ANONYMOUS
))
1519 static void buildFqTagCache (tagEntryInfo
*const tag
)
1521 getTagScopeInformation (tag
, NULL
, NULL
);
1524 static void writeTagEntry (const tagEntryInfo
*const tag
)
1528 Assert (tag
->kindIndex
!= KIND_GHOST_INDEX
);
1530 DebugStatement ( debugEntry (tag
); )
1533 if (getFilenameSeparator(Option
.useSlashAsFilenameSeparator
) == FILENAME_SEP_USE_SLASH
)
1535 Assert (((const tagEntryInfo
*)tag
)->inputFileName
);
1536 char *c
= (char *)(((tagEntryInfo
*const)tag
)->inputFileName
);
1539 if (*c
== PATH_SEPARATOR
)
1540 *c
= OUTPUT_PATH_SEPARATOR
;
1546 if (includeExtensionFlags ()
1547 && isXtagEnabled (XTAG_QUALIFIED_TAGS
)
1548 && doesInputLanguageRequestAutomaticFQTag ()
1549 && !isTagExtraBitMarked (tag
, XTAG_QUALIFIED_TAGS
)
1550 && !tag
->skipAutoFQEmission
)
1552 /* const is discarded to update the cache field of TAG. */
1553 buildFqTagCache ( (tagEntryInfo
*const)tag
);
1556 length
= writerWriteTag (TagFile
.mio
, tag
);
1560 ++TagFile
.numTags
.added
;
1561 rememberMaxLengths (strlen (tag
->name
), (size_t) length
);
1563 DebugStatement ( if (TagFile
.mio
) mio_flush (TagFile
.mio
); )
1565 abort_if_ferror (TagFile
.mio
);
1568 extern bool writePseudoTag (const ptagDesc
*desc
,
1569 const char *const fileName
,
1570 const char *const pattern
,
1571 const char *const parserName
)
1575 length
= writerWritePtag (TagFile
.mio
, desc
, fileName
,
1576 pattern
, parserName
);
1580 abort_if_ferror (TagFile
.mio
);
1582 ++TagFile
.numTags
.added
;
1583 rememberMaxLengths (strlen (desc
->name
), (size_t) length
);
1588 extern void corkTagFile(unsigned int corkFlags
)
1591 if (TagFile
.cork
== 1)
1593 TagFile
.corkFlags
= corkFlags
;
1594 TagFile
.corkQueue
= ptrArrayNew (deleteTagEnry
);
1595 tagEntryInfo
*nil
= newNilTagEntry (corkFlags
);
1596 ptrArrayAdd (TagFile
.corkQueue
, nil
);
1600 extern void uncorkTagFile(void)
1606 if (TagFile
.cork
> 0)
1609 for (i
= 1; i
< ptrArrayCount (TagFile
.corkQueue
); i
++)
1611 tagEntryInfo
*tag
= ptrArrayItem (TagFile
.corkQueue
, i
);
1613 if (!isTagWritable(tag
))
1616 writeTagEntry (tag
);
1618 if (doesInputLanguageRequestAutomaticFQTag ()
1619 && isXtagEnabled (XTAG_QUALIFIED_TAGS
)
1620 && !isTagExtraBitMarked (tag
, XTAG_QUALIFIED_TAGS
)
1621 && !tag
->skipAutoFQEmission
1622 && ((tag
->extensionFields
.scopeKindIndex
!= KIND_GHOST_INDEX
1623 && tag
->extensionFields
.scopeName
!= NULL
1624 && tag
->extensionFields
.scopeIndex
!= CORK_NIL
)
1625 || (tag
->extensionFields
.scopeKindIndex
== KIND_GHOST_INDEX
1626 && tag
->extensionFields
.scopeName
== NULL
1627 && tag
->extensionFields
.scopeIndex
== CORK_NIL
)))
1628 makeQualifiedTagEntry (tag
);
1631 ptrArrayDelete (TagFile
.corkQueue
);
1632 TagFile
.corkQueue
= NULL
;
1635 extern tagEntryInfo
*getEntryInCorkQueue (int n
)
1637 if ((CORK_NIL
< n
) && (((size_t)n
) < ptrArrayCount (TagFile
.corkQueue
)))
1638 return ptrArrayItem (TagFile
.corkQueue
, n
);
1643 extern tagEntryInfo
*getEntryOfNestingLevel (const NestingLevel
*nl
)
1647 return getEntryInCorkQueue (nl
->corkIndex
);
1650 extern size_t countEntryInCorkQueue (void)
1652 return ptrArrayCount (TagFile
.corkQueue
);
1655 extern void markTagPlaceholder (tagEntryInfo
*e
, bool placeholder
)
1657 e
->placeholder
= placeholder
;
1660 extern int makePlaceholder (const char *const name
)
1664 initTagEntry (&e
, name
, KIND_GHOST_INDEX
);
1665 markTagPlaceholder(&e
, true);
1668 * makePlaceholder may be called even before reading any bytes
1669 * from the input stream. In such case, initTagEntry fills
1670 * the lineNumber field of the placeholder tag with 0.
1671 * This breaks an assertion in makeTagEntry. Following adjustment
1672 * is for avoiding it.
1674 if (e
.lineNumber
== 0)
1677 return makeTagEntry (&e
);
1680 extern int makeTagEntry (const tagEntryInfo
*const tag
)
1683 Assert (tag
->name
!= NULL
);
1684 Assert(tag
->lineNumber
> 0);
1687 if (!isTagWritable (tag
))
1690 if (tag
->name
[0] == '\0' && (!tag
->placeholder
))
1692 if (!doesInputLanguageAllowNullTag())
1693 error (WARNING
, "ignoring null tag in %s(line: %lu)",
1694 getInputFileName (), tag
->lineNumber
);
1699 r
= queueTagEntry (tag
);
1701 writeTagEntry (tag
);
1704 notifyMakeTagEntry (tag
, r
);
1710 extern int makeQualifiedTagEntry (const tagEntryInfo
*const e
)
1716 static vString
*fqn
;
1718 if (isXtagEnabled (XTAG_QUALIFIED_TAGS
))
1721 markTagExtraBit (&x
, XTAG_QUALIFIED_TAGS
);
1723 fqn
= vStringNewOrClearWithAutoRelease (fqn
);
1725 if (e
->extensionFields
.scopeName
)
1727 vStringCatS (fqn
, e
->extensionFields
.scopeName
);
1728 xk
= e
->extensionFields
.scopeKindIndex
;
1729 sep
= scopeSeparatorFor (e
->langType
, e
->kindIndex
, xk
);
1730 vStringCatS (fqn
, sep
);
1734 /* This is an top level item. prepend a root separator
1735 if the kind of the item has. */
1736 sep
= scopeSeparatorFor (e
->langType
, e
->kindIndex
, KIND_GHOST_INDEX
);
1739 /* No root separator. The name of the
1740 optional tag and that of full qualified tag
1741 are the same; recording the full qualified tag
1746 vStringCatS (fqn
, sep
);
1748 vStringCatS (fqn
, e
->name
);
1750 x
.name
= vStringValue (fqn
);
1751 /* makeExtraTagEntry of c.c doesn't clear scope
1754 x
.extensionFields
.scopeKind
= NULL
;
1755 x
.extensionFields
.scopeName
= NULL
;
1756 x
.extensionFields
.scopeIndex
= CORK_NIL
;
1760 = isTagExtraBitMarked (&x
,
1764 pushLanguage(x
.langType
);
1766 r
= makeTagEntry (&x
);
1774 extern void setTagPositionFromTag (tagEntryInfo
*const dst
,
1775 const tagEntryInfo
*const src
)
1777 dst
->lineNumber
= src
->lineNumber
;
1778 dst
->filePosition
= src
->filePosition
;
1781 static void initTagEntryFull (tagEntryInfo
*const e
, const char *const name
,
1782 unsigned long lineNumber
,
1784 MIOPos filePosition
,
1785 const char *inputFileName
,
1787 roleBitsType roleBits
,
1788 const char *sourceFileName
,
1789 langType sourceLangType
,
1790 long sourceLineNumberDifference
)
1794 Assert (getInputFileName() != NULL
);
1796 memset (e
, 0, sizeof (tagEntryInfo
));
1797 e
->lineNumberEntry
= (bool) (Option
.locate
== EX_LINENUM
);
1798 e
->lineNumber
= lineNumber
;
1799 e
->boundaryInfo
= getNestedInputBoundaryInfo (lineNumber
);
1800 e
->langType
= langType_
;
1801 e
->filePosition
= filePosition
;
1802 e
->inputFileName
= inputFileName
;
1804 e
->extensionFields
.scopeLangType
= LANG_AUTO
;
1805 e
->extensionFields
.scopeKindIndex
= KIND_GHOST_INDEX
;
1806 e
->extensionFields
.scopeIndex
= CORK_NIL
;
1808 Assert (kindIndex
< 0 || kindIndex
< (int)countLanguageKinds(langType_
));
1809 e
->kindIndex
= kindIndex
;
1811 Assert (roleBits
== 0
1812 || (roleBits
< (makeRoleBit(countLanguageRoles(langType_
, kindIndex
)))));
1813 e
->extensionFields
.roleBits
= roleBits
;
1815 markTagExtraBit (e
, XTAG_REFERENCE_TAGS
);
1817 e
->extensionFields
.nth
= NO_NTH_FIELD
;
1819 if (doesParserRunAsGuest ())
1820 markTagExtraBit (e
, XTAG_GUEST
);
1821 if (doesSubparserRun ())
1822 markTagExtraBit (e
, XTAG_SUBPARSER
);
1824 e
->sourceLangType
= sourceLangType
;
1825 e
->sourceFileName
= sourceFileName
;
1826 e
->sourceLineNumberDifference
= sourceLineNumberDifference
;
1828 e
->usedParserFields
= 0;
1830 for ( i
= 0; i
< PRE_ALLOCATED_PARSER_FIELDS
; i
++ )
1831 e
->parserFields
[i
].ftype
= FIELD_UNKNOWN
;
1833 if (isParserMarkedNoEmission ())
1837 extern void initTagEntry (tagEntryInfo
*const e
, const char *const name
,
1840 initTagEntryFull(e
, name
,
1841 getInputLineNumber (),
1842 getInputLanguage (),
1843 getInputFilePosition (),
1844 getInputFileTagPath (),
1847 getSourceFileTagPath(),
1848 getSourceLanguage(),
1849 getSourceLineNumber() - getInputLineNumber ());
1852 extern void initRefTagEntry (tagEntryInfo
*const e
, const char *const name
,
1853 int kindIndex
, int roleIndex
)
1855 initForeignRefTagEntry (e
, name
, getInputLanguage (), kindIndex
, roleIndex
);
1858 extern void initForeignRefTagEntry (tagEntryInfo
*const e
, const char *const name
,
1860 int kindIndex
, int roleIndex
)
1862 initTagEntryFull(e
, name
,
1863 getInputLineNumber (),
1865 getInputFilePosition (),
1866 getInputFileTagPath (),
1868 makeRoleBit(roleIndex
),
1869 getSourceFileTagPath(),
1870 getSourceLanguage(),
1871 getSourceLineNumber() - getInputLineNumber ());
1874 static void markTagExtraBitFull (tagEntryInfo
*const tag
, xtagType extra
, bool mark
)
1877 unsigned int offset
;
1880 Assert (extra
!= XTAG_UNKNOWN
);
1882 if (extra
< XTAG_COUNT
)
1884 index
= (extra
/ 8);
1885 offset
= (extra
% 8);
1888 else if (tag
->extraDynamic
)
1890 Assert (extra
< countXtags ());
1892 index
= ((extra
- XTAG_COUNT
) / 8);
1893 offset
= ((extra
- XTAG_COUNT
) % 8);
1894 slot
= tag
->extraDynamic
;
1898 Assert (extra
< countXtags ());
1900 int n
= countXtags () - XTAG_COUNT
;
1901 tag
->extraDynamic
= xCalloc ((n
/ 8) + 1, uint8_t);
1902 if (!tag
->inCorkQueue
)
1903 PARSER_TRASH_BOX(tag
->extraDynamic
, eFree
);
1904 markTagExtraBitFull (tag
, extra
, mark
);
1909 slot
[ index
] |= (1 << offset
);
1911 slot
[ index
] &= ~(1 << offset
);
1914 extern void markTagExtraBit (tagEntryInfo
*const tag
, xtagType extra
)
1916 markTagExtraBitFull (tag
, extra
, true);
1919 extern void unmarkTagExtraBit (tagEntryInfo
*const tag
, xtagType extra
)
1921 markTagExtraBitFull (tag
, extra
, false);
1924 extern bool isTagExtraBitMarked (const tagEntryInfo
*const tag
, xtagType extra
)
1927 unsigned int offset
;
1928 const uint8_t *slot
;
1930 Assert (extra
!= XTAG_UNKNOWN
);
1932 if (extra
< XTAG_COUNT
)
1934 index
= (extra
/ 8);
1935 offset
= (extra
% 8);
1938 else if (!tag
->extraDynamic
)
1942 Assert (extra
< countXtags ());
1943 index
= ((extra
- XTAG_COUNT
) / 8);
1944 offset
= ((extra
- XTAG_COUNT
) % 8);
1945 slot
= tag
->extraDynamic
;
1947 return !! ((slot
[ index
]) & (1 << offset
));
1950 extern bool isTagExtra (const tagEntryInfo
*const tag
)
1952 for (unsigned int i
= 0; i
< countXtags(); i
++)
1953 if (isTagExtraBitMarked (tag
, i
))
1958 static void assignRoleFull(tagEntryInfo
*const e
, int roleIndex
, bool assign
)
1960 if (roleIndex
== ROLE_DEFINITION_INDEX
)
1964 e
->extensionFields
.roleBits
= 0;
1965 markTagExtraBitFull (e
, XTAG_REFERENCE_TAGS
, false);
1968 else if (roleIndex
> ROLE_DEFINITION_INDEX
)
1970 Assert (roleIndex
< (int)countLanguageRoles(e
->langType
, e
->kindIndex
));
1973 e
->extensionFields
.roleBits
|= (makeRoleBit(roleIndex
));
1975 e
->extensionFields
.roleBits
&= ~(makeRoleBit(roleIndex
));
1976 markTagExtraBitFull (e
, XTAG_REFERENCE_TAGS
, e
->extensionFields
.roleBits
);
1982 extern void assignRole(tagEntryInfo
*const e
, int roleIndex
)
1984 assignRoleFull(e
, roleIndex
, true);
1987 extern bool isRoleAssigned(const tagEntryInfo
*const e
, int roleIndex
)
1989 if (roleIndex
== ROLE_DEFINITION_INDEX
)
1990 return (!e
->extensionFields
.roleBits
);
1992 return (e
->extensionFields
.roleBits
& makeRoleBit(roleIndex
));
1995 extern unsigned long numTagsAdded(void)
1997 return TagFile
.numTags
.added
;
2000 extern void setNumTagsAdded (unsigned long nadded
)
2002 TagFile
.numTags
.added
= nadded
;
2005 extern unsigned long numTagsTotal(void)
2007 return TagFile
.numTags
.added
+ TagFile
.numTags
.prev
;
2010 extern unsigned long maxTagsLine (void)
2012 return (unsigned long)TagFile
.max
.line
;
2015 extern void invalidatePatternCache(void)
2017 TagFile
.patternCacheValid
= false;
2020 extern void tagFilePosition (MIOPos
*p
)
2022 /* mini-geany doesn't set TagFile.mio. */
2023 if (TagFile
.mio
== NULL
)
2026 if (mio_getpos (TagFile
.mio
, p
) == -1)
2027 error (FATAL
|PERROR
,
2028 "failed to get file position of the tag file\n");
2031 extern void setTagFilePosition (MIOPos
*p
, bool truncation
)
2033 /* mini-geany doesn't set TagFile.mio. */
2034 if (TagFile
.mio
== NULL
)
2040 t0
= mio_tell (TagFile
.mio
);
2042 if (mio_setpos (TagFile
.mio
, p
) == -1)
2043 error (FATAL
|PERROR
,
2044 "failed to set file position of the tag file\n");
2048 long t1
= mio_tell (TagFile
.mio
);
2049 if (!mio_try_resize (TagFile
.mio
, (size_t)t1
))
2050 error (FATAL
|PERROR
,
2051 "failed to truncate the tag file %ld -> %ld\n", t0
, t1
);
2055 extern const char* getTagFileDirectory (void)
2057 return TagFile
.directory
;
2060 static bool markAsPlaceholder (int index
, tagEntryInfo
*e
, void *data CTAGS_ATTR_UNUSED
)
2063 markAllEntriesInScopeAsPlaceholder (index
);
2067 extern void markAllEntriesInScopeAsPlaceholder (int index
)
2069 foreachEntriesInScope (index
, NULL
, markAsPlaceholder
, NULL
);