xref: /Universal-ctags/main/entry.c (revision aaaac7eeac8399141aa8e6d9e6ec0379931848b2)
1 /*
2 *   Copyright (c) 1996-2002, Darren Hiebert
3 *
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.
6 *
7 *   This module contains functions for creating tag entries.
8 */
9 
10 /*
11 *   INCLUDE FILES
12 */
13 #include "general.h"  /* must always come first */
14 
15 #include <string.h>
16 #include <ctype.h>        /* to define isspace () */
17 #include <errno.h>
18 
19 #if defined (HAVE_SYS_TYPES_H)
20 # include <sys/types.h>	  /* to declare off_t on some hosts */
21 #endif
22 #if defined (HAVE_TYPES_H)
23 # include <types.h>       /* to declare off_t on some hosts */
24 #endif
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>      /* to declare close (), ftruncate (), truncate () */
27 #endif
28 
29 /*  These header files provide for the functions necessary to do file
30  *  truncation.
31  */
32 #ifdef HAVE_FCNTL_H
33 # include <fcntl.h>
34 #endif
35 #ifdef HAVE_IO_H
36 # include <io.h>
37 #endif
38 
39 #include <stdint.h>
40 #include <limits.h>  /* to define INT_MAX */
41 
42 #include "debug.h"
43 #include "entry_p.h"
44 #include "field.h"
45 #include "fmt_p.h"
46 #include "kind.h"
47 #include "nestlevel.h"
48 #include "options_p.h"
49 #include "ptag_p.h"
50 #include "rbtree.h"
51 #include "read.h"
52 #include "read_p.h"
53 #include "routines.h"
54 #include "routines_p.h"
55 #include "parse_p.h"
56 #include "ptrarray.h"
57 #include "sort_p.h"
58 #include "strlist.h"
59 #include "subparser_p.h"
60 #include "trashbox.h"
61 #include "writer_p.h"
62 #include "xtag_p.h"
63 
64 /*
65 *   MACROS
66 */
67 
68 /*
69  *  Portability defines
70  */
71 #if !defined(HAVE_TRUNCATE) && !defined(HAVE_FTRUNCATE) && !defined(HAVE_CHSIZE)
72 # define USE_REPLACEMENT_TRUNCATE
73 #endif
74 
75 /*  Hack for ridiculous practice of Microsoft Visual C++.
76  */
77 #if defined (WIN32) && defined (_MSC_VER)
78 # define chsize         _chsize
79 # define open           _open
80 # define close          _close
81 # define O_RDWR         _O_RDWR
82 #endif
83 
84 
85 /*  Maintains the state of the tag file.
86  */
87 typedef struct eTagFile {
88 	char *name;
89 	char *directory;
90 	MIO *mio;
91 	struct sNumTags { unsigned long added, prev; } numTags;
92 	struct sMax { size_t line, tag; } max;
93 	vString *vLine;
94 
95 	int cork;
96 	unsigned int corkFlags;
97 	ptrArray *corkQueue;
98 
99 	bool patternCacheValid;
100 } tagFile;
101 
102 typedef struct sTagEntryInfoX  {
103 	tagEntryInfo slot;
104 	int corkIndex;
105 	struct rb_root symtab;
106 	struct rb_node symnode;
107 } tagEntryInfoX;
108 
109 /*
110 *   DATA DEFINITIONS
111 */
112 
113 static tagFile TagFile = {
114     NULL,               /* tag file name */
115     NULL,               /* tag file directory (absolute) */
116     NULL,               /* file pointer */
117     { 0, 0 },           /* numTags */
118     { 0, 0 },        /* max */
119     NULL,                /* vLine */
120     .cork = false,
121     .corkQueue = NULL,
122     .patternCacheValid = false,
123 };
124 
125 static bool TagsToStdout = false;
126 
127 /*
128 *   FUNCTION PROTOTYPES
129 */
130 #ifdef NEED_PROTO_TRUNCATE
131 extern int truncate (const char *path, off_t length);
132 #endif
133 
134 #ifdef NEED_PROTO_FTRUNCATE
135 extern int ftruncate (int fd, off_t length);
136 #endif
137 
138 /*
139 *   FUNCTION DEFINITIONS
140 */
141 
freeTagFileResources(void)142 extern void freeTagFileResources (void)
143 {
144 	if (TagFile.directory != NULL)
145 		eFree (TagFile.directory);
146 	vStringDelete (TagFile.vLine);
147 }
148 
tagFileName(void)149 extern const char *tagFileName (void)
150 {
151 	return TagFile.name;
152 }
153 
154 /*
155 *   Pseudo tag support
156 */
157 
abort_if_ferror(MIO * const mio)158 extern void abort_if_ferror(MIO *const mio)
159 {
160 	if (mio != NULL && mio_error (mio))
161 		error (FATAL | PERROR, "cannot write tag file");
162 }
163 
rememberMaxLengths(const size_t nameLength,const size_t lineLength)164 static void rememberMaxLengths (const size_t nameLength, const size_t lineLength)
165 {
166 	if (nameLength > TagFile.max.tag)
167 		TagFile.max.tag = nameLength;
168 
169 	if (lineLength > TagFile.max.line)
170 		TagFile.max.line = lineLength;
171 }
172 
addCommonPseudoTags(void)173 static void addCommonPseudoTags (void)
174 {
175 	for (int i = 0; i < PTAG_COUNT; i++)
176 	{
177 		if (isPtagCommonInParsers (i))
178 			makePtagIfEnabled (i, LANG_IGNORE, &Option);
179 	}
180 }
181 
makeFileTag(const char * const fileName)182 extern void makeFileTag (const char *const fileName)
183 {
184 	tagEntryInfo tag;
185 
186 	if (!isXtagEnabled(XTAG_FILE_NAMES))
187 		return;
188 
189 	initTagEntry (&tag, baseFilename (fileName), KIND_FILE_INDEX);
190 
191 	tag.isFileEntry     = true;
192 	tag.lineNumberEntry = true;
193 	markTagExtraBit (&tag, XTAG_FILE_NAMES);
194 
195 	tag.lineNumber = 1;
196 	if (isFieldEnabled (FIELD_END_LINE))
197 	{
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)
202 			; /* Do nothing */
203 		tag.extensionFields.endLine = getInputLineNumber ();
204 	}
205 
206 	if (isFieldEnabled (FIELD_EPOCH))
207 		tag.extensionFields.epoch = getInputFileMtime ();
208 
209 	makeTagEntry (&tag);
210 }
211 
updateSortedFlag(const char * const line,MIO * const mio,MIOPos startOfLine)212 static void updateSortedFlag (
213 		const char *const line, MIO *const mio, MIOPos startOfLine)
214 {
215 	const char *const tab = strchr (line, '\t');
216 
217 	if (tab != NULL)
218 	{
219 		const long boolOffset = tab - line + 1;  /* where it should be */
220 
221 		if (line [boolOffset] == '0'  ||  line [boolOffset] == '1')
222 		{
223 			MIOPos nextLine;
224 
225 			if (mio_getpos (mio, &nextLine) == -1 || mio_setpos (mio, &startOfLine) == -1)
226 				error (WARNING, "Failed to update 'sorted' pseudo-tag");
227 			else
228 			{
229 				MIOPos flagLocation;
230 				int c, d;
231 
232 				do
233 					c = mio_getc (mio);
234 				while (c != '\t'  &&  c != '\n');
235 				mio_getpos (mio, &flagLocation);
236 				d = mio_getc (mio);
237 				if (c == '\t'  &&  (d == '0'  ||  d == '1')  &&
238 					d != (int) Option.sorted)
239 				{
240 					mio_setpos (mio, &flagLocation);
241 					mio_putc (mio, Option.sorted == SO_FOLDSORTED ? '2' :
242 						(Option.sorted == SO_SORTED ? '1' : '0'));
243 				}
244 				mio_setpos (mio, &nextLine);
245 			}
246 		}
247 	}
248 }
249 
250 /*  Look through all line beginning with "!_TAG_FILE", and update those which
251  *  require it.
252  */
updatePseudoTags(MIO * const mio)253 static long unsigned int updatePseudoTags (MIO *const mio)
254 {
255 	enum { maxEntryLength = 20 };
256 	char entry [maxEntryLength + 1];
257 	unsigned long linesRead = 0;
258 	MIOPos startOfLine;
259 	size_t entryLength;
260 	const char *line;
261 
262 	sprintf (entry, "%sTAG_FILE", PSEUDO_TAG_PREFIX);
263 	entryLength = strlen (entry);
264 	Assert (entryLength < maxEntryLength);
265 
266 	mio_getpos (mio, &startOfLine);
267 	line = readLineRaw (TagFile.vLine, mio);
268 	while (line != NULL  &&  line [0] == entry [0])
269 	{
270 		++linesRead;
271 		if (strncmp (line, entry, entryLength) == 0)
272 		{
273 			char tab, classType [16];
274 
275 			if (sscanf (line + entryLength, "%15s%c", classType, &tab) == 2  &&
276 				tab == '\t')
277 			{
278 				if (strcmp (classType, "_SORTED") == 0)
279 					updateSortedFlag (line, mio, startOfLine);
280 			}
281 			mio_getpos (mio, &startOfLine);
282 		}
283 		line = readLineRaw (TagFile.vLine, mio);
284 	}
285 	while (line != NULL)  /* skip to end of file */
286 	{
287 		++linesRead;
288 		line = readLineRaw (TagFile.vLine, mio);
289 	}
290 	return linesRead;
291 }
292 
293 /*
294  *  Tag file management
295  */
296 
isValidTagAddress(const char * const excmd)297 static bool isValidTagAddress (const char *const excmd)
298 {
299 	bool isValid = false;
300 
301 	if (strchr ("/?", excmd [0]) != NULL)
302 		isValid = true;
303 	else
304 	{
305 		char *address = xMalloc (strlen (excmd) + 1, char);
306 		if (sscanf (excmd, "%[^;\n]", address) == 1  &&
307 			strspn (address,"0123456789") == strlen (address))
308 				isValid = true;
309 		eFree (address);
310 	}
311 	return isValid;
312 }
313 
isCtagsLine(const char * const line)314 static bool isCtagsLine (const char *const line)
315 {
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);
320 
321 	if (fields == NULL)
322 		error (FATAL, "Cannot analyze tag file");
323 	else
324 	{
325 #define field(x)		(fields + ((size_t) (x) * fieldLength))
326 
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));
331 
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
335 		 *  acceptable.
336 		 *
337 		 *  These conditions will reject tag-looking lines like:
338 		 *      int a;        <C-comment>
339 		 *      #define LABEL <C-comment>
340 		 */
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)))
347 				ok = true;
348 
349 		eFree (fields);
350 	}
351 	return ok;
352 }
353 
isEtagsLine(const char * const line)354 static bool isEtagsLine (const char *const line)
355 {
356 	bool result = false;
357 	if (line [0] == '\f')
358 		result = (bool) (line [1] == '\n'  ||  line [1] == '\r');
359 	return result;
360 }
361 
isTagFile(const char * const filename)362 static bool isTagFile (const char *const filename)
363 {
364 	bool ok = false;  /* we assume not unless confirmed */
365 	MIO *const mio = mio_new_file (filename, "rb");
366 
367 	if (mio == NULL  &&  errno == ENOENT)
368 		ok = true;
369 	else if (mio != NULL)
370 	{
371 		const char *line = readLineRaw (TagFile.vLine, mio);
372 
373 		if (line == NULL)
374 			ok = true;
375 		else
376 			ok = (bool) (isCtagsLine (line) || isEtagsLine (line));
377 		mio_unref (mio);
378 	}
379 	return ok;
380 }
381 
openTagFile(void)382 extern void openTagFile (void)
383 {
384 	setDefaultTagFileName ();
385 	TagsToStdout = isDestinationStdout ();
386 
387 	if (TagFile.vLine == NULL)
388 		TagFile.vLine = vStringNew ();
389 
390 	/*  Open the tags file.
391 	 */
392 	if (TagsToStdout)
393 	{
394 		if (Option.interactive == INTERACTIVE_SANDBOX)
395 		{
396 			TagFile.mio = mio_new_memory (NULL, 0, eRealloc, eFreeNoNullCheck);
397 			TagFile.name = NULL;
398 		}
399 		else
400 			TagFile.mio = tempFile ("w+", &TagFile.name);
401 		if (isXtagEnabled (XTAG_PSEUDO_TAGS))
402 			addCommonPseudoTags ();
403 	}
404 	else
405 	{
406 		bool fileExists;
407 
408 		TagFile.name = eStrdup (Option.tagFileName);
409 		fileExists = doesFileExist (TagFile.name);
410 		if (fileExists  &&  ! isTagFile (TagFile.name))
411 			error (FATAL,
412 			  "\"%s\" doesn't look like a tag file; I refuse to overwrite it.",
413 				  TagFile.name);
414 
415 		if (Option.etags)
416 		{
417 			if (Option.append  &&  fileExists)
418 				TagFile.mio = mio_new_file (TagFile.name, "a+b");
419 			else
420 				TagFile.mio = mio_new_file (TagFile.name, "w+b");
421 		}
422 		else
423 		{
424 			if (Option.append  &&  fileExists)
425 			{
426 				TagFile.mio = mio_new_file (TagFile.name, "r+");
427 				if (TagFile.mio != NULL)
428 				{
429 					TagFile.numTags.prev = updatePseudoTags (TagFile.mio);
430 					mio_unref (TagFile.mio);
431 					TagFile.mio = mio_new_file (TagFile.name, "a+");
432 				}
433 			}
434 			else
435 			{
436 				TagFile.mio = mio_new_file (TagFile.name, "w");
437 				if (TagFile.mio != NULL && isXtagEnabled (XTAG_PSEUDO_TAGS))
438 					addCommonPseudoTags ();
439 			}
440 		}
441 		if (TagFile.mio == NULL)
442 			error (FATAL | PERROR, "cannot open tag file");
443 	}
444 
445 	if (TagFile.directory == NULL)
446 	{
447 		if (TagsToStdout)
448 			TagFile.directory = eStrdup (CurrentDirectory);
449 		else
450 			TagFile.directory = absoluteDirname (TagFile.name);
451 	}
452 }
453 
454 #ifdef USE_REPLACEMENT_TRUNCATE
455 
copyBytes(MIO * const fromMio,MIO * const toMio,const long size)456 static void copyBytes (MIO* const fromMio, MIO* const toMio, const long size)
457 {
458 	enum { BufferSize = 1000 };
459 	long toRead, numRead;
460 	char* buffer = xMalloc (BufferSize, char);
461 	long remaining = size;
462 	do
463 	{
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");
469 		if (remaining > 0)
470 			remaining -= numRead;
471 	} while (numRead == toRead  &&  remaining != 0);
472 	eFree (buffer);
473 }
474 
copyFile(const char * const from,const char * const to,const long size)475 static void copyFile (const char *const from, const char *const to, const long size)
476 {
477 	MIO* const fromMio = mio_new_file (from, "rb");
478 	if (fromMio == NULL)
479 		error (FATAL | PERROR, "cannot open file to copy");
480 	else
481 	{
482 		MIO* const toMio = mio_new_file (to, "wb");
483 		if (toMio == NULL)
484 			error (FATAL | PERROR, "cannot open copy destination");
485 		else
486 		{
487 			copyBytes (fromMio, toMio, size);
488 			mio_unref (toMio);
489 		}
490 		mio_unref (fromMio);
491 	}
492 }
493 
494 /*  Replacement for missing library function.
495  */
replacementTruncate(const char * const name,const long size)496 static int replacementTruncate (const char *const name, const long size)
497 {
498 #define WHOLE_FILE  -1L
499 	char *tempName = NULL;
500 	MIO *mio = tempFile ("w", &tempName);
501 	mio_unref (mio);
502 	copyFile (name, tempName, size);
503 	copyFile (tempName, name, WHOLE_FILE);
504 	remove (tempName);
505 	eFree (tempName);
506 
507 	return 0;
508 }
509 
510 #endif
511 
512 #ifndef EXTERNAL_SORT
internalSortTagFile(void)513 static void internalSortTagFile (void)
514 {
515 	MIO *mio;
516 
517 	/*  Open/Prepare the tag file and place its lines into allocated buffers.
518 	 */
519 	if (TagsToStdout)
520 	{
521 		mio = TagFile.mio;
522 		mio_seek (mio, 0, SEEK_SET);
523 	}
524 	else
525 	{
526 		mio = mio_new_file (tagFileName (), "r");
527 		if (mio == NULL)
528 			failedSort (mio, NULL);
529 	}
530 
531 	internalSortTags (TagsToStdout,
532 			  mio,
533 			  TagFile.numTags.added + TagFile.numTags.prev);
534 
535 	if (! TagsToStdout)
536 		mio_unref (mio);
537 }
538 #endif
539 
sortTagFile(void)540 static void sortTagFile (void)
541 {
542 	if (TagFile.numTags.added > 0L)
543 	{
544 		if (Option.sorted != SO_UNSORTED)
545 		{
546 			verbose ("sorting tag file\n");
547 #ifdef EXTERNAL_SORT
548 			externalSortTags (TagsToStdout, TagFile.mio);
549 #else
550 			internalSortTagFile ();
551 #endif
552 		}
553 		else if (TagsToStdout)
554 			catFile (TagFile.mio);
555 	}
556 }
557 
resizeTagFile(const long newSize)558 static void resizeTagFile (const long newSize)
559 {
560 	int result;
561 
562 	if (!TagFile.name)
563 	{
564 		mio_try_resize (TagFile.mio, newSize);
565 		return;
566 	}
567 
568 #ifdef USE_REPLACEMENT_TRUNCATE
569 	result = replacementTruncate (TagFile.name, newSize);
570 #else
571 # ifdef HAVE_TRUNCATE
572 	result = truncate (TagFile.name, (off_t) newSize);
573 # else
574 	const int fd = open (TagFile.name, O_RDWR);
575 
576 	if (fd == -1)
577 		result = -1;
578 	else
579 	{
580 #  ifdef HAVE_FTRUNCATE
581 		result = ftruncate (fd, (off_t) newSize);
582 #  else
583 #   ifdef HAVE_CHSIZE
584 		result = chsize (fd, newSize);
585 #   endif
586 #  endif
587 		close (fd);
588 	}
589 # endif
590 #endif
591 	if (result == -1)
592 		fprintf (stderr, "Cannot shorten tag file: errno = %d\n", errno);
593 }
594 
writeEtagsIncludes(MIO * const mio)595 static void writeEtagsIncludes (MIO *const mio)
596 {
597 	if (Option.etagsInclude)
598 	{
599 		unsigned int i;
600 		for (i = 0  ;  i < stringListCount (Option.etagsInclude)  ;  ++i)
601 		{
602 			vString *item = stringListItem (Option.etagsInclude, i);
603 			mio_printf (mio, "\f\n%s,include\n", vStringValue (item));
604 		}
605 	}
606 }
607 
closeTagFile(const bool resize)608 extern void closeTagFile (const bool resize)
609 {
610 	long desiredSize, size;
611 
612 	if (Option.etags)
613 		writeEtagsIncludes (TagFile.mio);
614 	mio_flush (TagFile.mio);
615 
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);
620 	if (! TagsToStdout)
621 		/* The tag file should be closed before resizing. */
622 		if (mio_unref (TagFile.mio) != 0)
623 			error (FATAL | PERROR, "cannot close tag file");
624 
625 	if (resize  &&  desiredSize < size)
626 	{
627 		DebugStatement (
628 			debugPrintf (DEBUG_STATUS, "shrinking %s from %ld to %ld bytes\n",
629 				TagFile.name? TagFile.name: "<mio>", size, desiredSize); )
630 		resizeTagFile (desiredSize);
631 	}
632 	sortTagFile ();
633 	if (TagsToStdout)
634 	{
635 		if (mio_unref (TagFile.mio) != 0)
636 			error (FATAL | PERROR, "cannot close tag file");
637 		if (TagFile.name)
638 			remove (TagFile.name);  /* remove temporary file */
639 	}
640 
641 	TagFile.mio = NULL;
642 	if (TagFile.name)
643 		eFree (TagFile.name);
644 	TagFile.name = NULL;
645 }
646 
647 /*
648  *  Tag entry management
649  */
650 
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.
655  */
appendInputLine(int putc_func (char,void *),const char * const line,unsigned int patternLengthLimit,void * data,bool * omitted)656 static size_t appendInputLine (int putc_func (char , void *), const char *const line,
657 							   unsigned int patternLengthLimit,
658 							   void * data, bool *omitted)
659 {
660 	size_t length = 0;
661 	const char *p;
662 	int extraLength = 0;
663 
664 	/*  Write everything up to, but not including, a line end character.
665 	 */
666 	*omitted = false;
667 	for (p = line  ;  *p != '\0'  ;  ++p)
668 	{
669 		const int next = *(p + 1);
670 		const int c = *p;
671 
672 		if (c == CRETURN  ||  c == NEWLINE)
673 			break;
674 
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))
680 		{
681 			*omitted = true;
682 			break;
683 		}
684 		/*  If character is '\', or a terminal '$', then quote it.
685 		 */
686 		if (c == BACKSLASH  ||  c == (Option.backward ? '?' : '/')  ||
687 			(c == '$'  &&  (next == NEWLINE  ||  next == CRETURN)))
688 		{
689 			putc_func (BACKSLASH, data);
690 			++length;
691 		}
692 		putc_func (c, data);
693 		++length;
694 	}
695 
696 	return length;
697 }
698 
vstring_putc(char c,void * data)699 static int vstring_putc (char c, void *data)
700 {
701 	vString *str = data;
702 	vStringPut (str, c);
703 	return 1;
704 }
705 
vstring_puts(const char * s,void * data)706 static int vstring_puts (const char* s, void *data)
707 {
708 	vString *str = data;
709 	size_t len = vStringLength (str);
710 	vStringCatS (str, s);
711 	return (int) (vStringLength (str) - len);
712 }
713 
714 #ifdef DEBUG
isPosSet(MIOPos pos)715 static bool isPosSet(MIOPos pos)
716 {
717 	char * p = (char *)&pos;
718 	bool r = false;
719 	unsigned int i;
720 
721 	for (i = 0; i < sizeof(pos); i++)
722 		r |= p[i];
723 	return r;
724 }
725 #endif
726 
readLineFromBypassForTag(vString * const vLine,const tagEntryInfo * const tag,long * const pSeekValue)727 extern char *readLineFromBypassForTag (vString *const vLine, const tagEntryInfo *const tag,
728 				   long *const pSeekValue)
729 {
730 	Assert (isPosSet (tag->filePosition) || (tag->pattern == NULL));
731 	return readLineFromBypass (vLine, tag->filePosition, pSeekValue);
732 }
733 
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).
737  */
truncateTagLineAfterTag(char * const line,const char * const token,const bool discardNewline)738 extern size_t truncateTagLineAfterTag (
739 		char *const line, const char *const token, const bool discardNewline)
740 {
741 	size_t len = 0;
742 	char *p = strstr (line, token);
743 
744 	if (p != NULL)
745 	{
746 		p += strlen (token);
747 		if (*p != '\0'  &&  ! (*p == '\n'  &&  discardNewline))
748 			++p;    /* skip past character terminating character */
749 		*p = '\0';
750 		len = p - line;
751 	}
752 
753 	return len;
754 }
755 
getFullQualifiedScopeNameFromCorkQueue(const tagEntryInfo * inner_scope)756 static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_scope)
757 {
758 
759 	int kindIndex = KIND_GHOST_INDEX;
760 	langType lang;
761 	const tagEntryInfo *scope = inner_scope;
762 	const tagEntryInfo *root_scope = NULL;
763 	stringList *queue = stringListNew ();
764 	vString *v;
765 	vString *n;
766 	unsigned int c;
767 	const char *sep;
768 
769 	while (scope)
770 	{
771 		if (!scope->placeholder)
772 		{
773 			if (kindIndex != KIND_GHOST_INDEX)
774 			{
775 				sep = scopeSeparatorFor (lang, kindIndex, scope->kindIndex);
776 				v = vStringNewInit (sep);
777 				stringListAdd (queue, v);
778 			}
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;
784 			root_scope = scope;
785 		}
786 		int scopeIndex = scope->extensionFields.scopeIndex;
787 		scope =  getEntryInCorkQueue (scopeIndex);
788 
789 		if (scope && scope->extensionFields.scopeIndex == scopeIndex)
790 		{
791 			error (WARNING,
792 				   "interanl error: scope information made a loop structure: %s in %s:%lu",
793 				   scope->name, scope->inputFileName, scope->lineNumber);
794 			/* Force break this while-loop. */
795 			scope = NULL;
796 		}
797 	}
798 
799 	n = vStringNew ();
800 	sep = root_scope? scopeSeparatorFor (root_scope->langType, root_scope->kindIndex, KIND_GHOST_INDEX): NULL;
801 	if (sep)
802 		vStringCatS(n, sep);
803 
804 	while ((c = stringListCount (queue)) > 0)
805 	{
806 		v = stringListLast (queue);
807 		vStringCat (n, v);
808 		vStringDelete (v);
809 		stringListRemoveLast (queue);
810 	}
811 	stringListDelete (queue);
812 
813 	return vStringDeleteUnwrap (n);
814 }
815 
getTagScopeInformation(tagEntryInfo * const tag,const char ** kind,const char ** name)816 extern void getTagScopeInformation (tagEntryInfo *const tag,
817 				    const char **kind, const char **name)
818 {
819 	if (kind)
820 		*kind = NULL;
821 	if (name)
822 		*name = NULL;
823 
824 	const tagEntryInfo * scope = getEntryInCorkQueue (tag->extensionFields.scopeIndex);
825 	if (tag->extensionFields.scopeKindIndex == KIND_GHOST_INDEX
826 	    && tag->extensionFields.scopeName == NULL
827 	    && scope
828 	    && ptrArrayCount (TagFile.corkQueue) > 0)
829 	{
830 		char *full_qualified_scope_name = getFullQualifiedScopeNameFromCorkQueue(scope);
831 		Assert (full_qualified_scope_name);
832 
833 		/* Make the information reusable to generate full qualified entry, and xformat output*/
834 		tag->extensionFields.scopeLangType = scope->langType;
835 		tag->extensionFields.scopeKindIndex = scope->kindIndex;
836 		tag->extensionFields.scopeName = full_qualified_scope_name;
837 	}
838 
839 	if (tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX  &&
840 	    tag->extensionFields.scopeName != NULL)
841 	{
842 		if (kind)
843 		{
844 			langType lang = (tag->extensionFields.scopeLangType == LANG_AUTO)
845 				? tag->langType
846 				: tag->extensionFields.scopeLangType;
847 			kindDefinition *kdef = getLanguageKind (lang,
848 													tag->extensionFields.scopeKindIndex);
849 			*kind = kdef->name;
850 		}
851 		if (name)
852 			*name = tag->extensionFields.scopeName;
853 	}
854 }
855 
856 
makePatternStringCommon(const tagEntryInfo * const tag,int (* putc_func)(char,void *),int (* puts_func)(const char *,void *),void * output)857 static int   makePatternStringCommon (const tagEntryInfo *const tag,
858 				      int (* putc_func) (char , void *),
859 				      int (* puts_func) (const char* , void *),
860 				      void *output)
861 {
862 	int length = 0;
863 
864 	char *line;
865 	int searchChar;
866 	const char *terminator;
867 	bool  omitted;
868 	size_t line_len;
869 
870 	bool making_cache = false;
871 	int (* puts_o_func)(const char* , void *);
872 	void * o_output;
873 
874 	static vString *cached_pattern;
875 	static MIOPos   cached_location;
876 	if (TagFile.patternCacheValid
877 	    && (! tag->truncateLineAfterTag)
878 	    && (memcmp (&tag->filePosition, &cached_location, sizeof(MIOPos)) == 0))
879 		return puts_func (vStringValue (cached_pattern), output);
880 
881 	line = readLineFromBypassForTag (TagFile.vLine, tag, NULL);
882 	if (line == NULL)
883 	{
884 		/* This can be occurs if the size of input file is zero, and
885 		   an empty regex pattern (//) matches to the input. */
886 		line = "";
887 		line_len = 0;
888 	}
889 	else
890 		line_len = vStringLength (TagFile.vLine);
891 
892 	if (tag->truncateLineAfterTag)
893 	{
894 		size_t truncted_len;
895 
896 		truncted_len = truncateTagLineAfterTag (line, tag->name, false);
897 		if (truncted_len > 0)
898 			line_len = truncted_len;
899 	}
900 
901 	searchChar = Option.backward ? '?' : '/';
902 	terminator = (line_len > 0 && (line [line_len - 1] == '\n')) ? "$": "";
903 
904 	if (!tag->truncateLineAfterTag)
905 	{
906 		making_cache = true;
907 		cached_pattern = vStringNewOrClearWithAutoRelease (cached_pattern);
908 
909 		puts_o_func = puts_func;
910 		o_output    = output;
911 		putc_func   = vstring_putc;
912 		puts_func   = vstring_puts;
913 		output      = cached_pattern;
914 	}
915 
916 	length += putc_func(searchChar, output);
917 	if ((tag->boundaryInfo & BOUNDARY_START) == 0)
918 		length += putc_func('^', output);
919 	length += appendInputLine (putc_func, line, Option.patternLengthLimit,
920 							   output, &omitted);
921 	length += puts_func (omitted? "": terminator, output);
922 	length += putc_func (searchChar, output);
923 
924 	if (making_cache)
925 	{
926 		puts_o_func (vStringValue (cached_pattern), o_output);
927 		cached_location = tag->filePosition;
928 		TagFile.patternCacheValid = true;
929 	}
930 
931 	return length;
932 }
933 
makePatternString(const tagEntryInfo * const tag)934 extern char* makePatternString (const tagEntryInfo *const tag)
935 {
936 	vString* pattern = vStringNew ();
937 	makePatternStringCommon (tag, vstring_putc, vstring_puts, pattern);
938 	return vStringDeleteUnwrap (pattern);
939 }
940 
tagFieldNew(fieldType ftype,const char * value,bool valueOwner)941 static tagField * tagFieldNew(fieldType ftype, const char *value, bool valueOwner)
942 {
943 	tagField *f = xMalloc (1, tagField);
944 
945 	f->ftype = ftype;
946 	f->value = value;
947 	f->valueOwner = valueOwner;
948 	return f;
949 }
950 
tagFieldDelete(tagField * f)951 static void tagFieldDelete (tagField * f)
952 {
953 	if (f->valueOwner)
954 		eFree((void *)f->value);
955 	eFree (f);
956 }
957 
attachParserFieldGeneric(tagEntryInfo * const tag,fieldType ftype,const char * value,bool valueOwner)958 static void attachParserFieldGeneric (tagEntryInfo *const tag, fieldType ftype, const char * value,
959 									  bool valueOwner)
960 {
961 	if (tag->usedParserFields < PRE_ALLOCATED_PARSER_FIELDS)
962 	{
963 		tag->parserFields [tag->usedParserFields].ftype = ftype;
964 		tag->parserFields [tag->usedParserFields].value = value;
965 		tag->parserFields [tag->usedParserFields].valueOwner = valueOwner;
966 		tag->usedParserFields++;
967 	}
968 	else if (tag->parserFieldsDynamic == NULL)
969 	{
970 		tag->parserFieldsDynamic = ptrArrayNew((ptrArrayDeleteFunc)tagFieldDelete);
971 		PARSER_TRASH_BOX(tag->parserFieldsDynamic, ptrArrayDelete);
972 		attachParserFieldGeneric (tag, ftype, value, valueOwner);
973 	}
974 	else
975 	{
976 		tagField *f = tagFieldNew (ftype, value, valueOwner);
977 		ptrArrayAdd(tag->parserFieldsDynamic, f);
978 		tag->usedParserFields++;
979 	}
980 }
981 
attachParserField(tagEntryInfo * const tag,bool inCorkQueue,fieldType ftype,const char * value)982 extern void attachParserField (tagEntryInfo *const tag, bool inCorkQueue, fieldType ftype, const char * value)
983 {
984 	Assert (tag != NULL);
985 
986 	if (inCorkQueue)
987 	{
988 		const char * v;
989 		v = eStrdup (value);
990 
991 		bool dynfields_allocated = tag->parserFieldsDynamic? true: false;
992 		attachParserFieldGeneric (tag, ftype, v, true);
993 		if (!dynfields_allocated && tag->parserFieldsDynamic)
994 			PARSER_TRASH_BOX_TAKE_BACK(tag->parserFieldsDynamic);
995 	}
996 	else
997 		attachParserFieldGeneric (tag, ftype, value, false);
998 }
999 
attachParserFieldToCorkEntry(int index,fieldType ftype,const char * value)1000 extern void attachParserFieldToCorkEntry (int index,
1001 					 fieldType ftype,
1002 					 const char *value)
1003 {
1004 	tagEntryInfo * tag = getEntryInCorkQueue (index);
1005 	if (tag)
1006 		attachParserField (tag, true, ftype, value);
1007 }
1008 
getParserFieldForIndex(const tagEntryInfo * tag,int index)1009 extern const tagField* getParserFieldForIndex (const tagEntryInfo * tag, int index)
1010 {
1011 	if (index < 0
1012 		|| tag->usedParserFields <= ((unsigned int)index) )
1013 		return NULL;
1014 	else if (index < PRE_ALLOCATED_PARSER_FIELDS)
1015 		return tag->parserFields + index;
1016 	else
1017 	{
1018 		unsigned int n = index - PRE_ALLOCATED_PARSER_FIELDS;
1019 		return ptrArrayItem(tag->parserFieldsDynamic, n);
1020 	}
1021 }
1022 
getParserFieldValueForType(tagEntryInfo * const tag,fieldType ftype)1023 extern const char* getParserFieldValueForType (tagEntryInfo *const tag, fieldType ftype)
1024 {
1025 	for (int i = 0; i < tag->usedParserFields; i++)
1026 	{
1027 		const tagField *f = getParserFieldForIndex (tag, i);
1028 		if (f && f->ftype == ftype)
1029 			return f->value;
1030 	}
1031 	return NULL;
1032 }
1033 
copyParserFields(const tagEntryInfo * const tag,tagEntryInfo * slot)1034 static void copyParserFields (const tagEntryInfo *const tag, tagEntryInfo* slot)
1035 {
1036 	unsigned int i;
1037 	const char* value;
1038 
1039 	for (i = 0; i < tag->usedParserFields; i++)
1040 	{
1041 		const tagField *f = getParserFieldForIndex (tag, i);
1042 		Assert(f);
1043 
1044 		value = f->value;
1045 		if (value)
1046 			value = eStrdup (value);
1047 
1048 		attachParserFieldGeneric (slot,
1049 								  f->ftype,
1050 								  value,
1051 								  true);
1052 	}
1053 
1054 }
1055 
newNilTagEntry(unsigned int corkFlags)1056 static tagEntryInfo *newNilTagEntry (unsigned int corkFlags)
1057 {
1058 	tagEntryInfoX *x = xCalloc (1, tagEntryInfoX);
1059 	x->corkIndex = CORK_NIL;
1060 	x->symtab = RB_ROOT;
1061 	x->slot.kindIndex = KIND_FILE_INDEX;
1062 	return &(x->slot);
1063 }
1064 
copyTagEntry(const tagEntryInfo * const tag,unsigned int corkFlags)1065 static tagEntryInfoX *copyTagEntry (const tagEntryInfo *const tag,
1066 								   unsigned int corkFlags)
1067 {
1068 	tagEntryInfoX *x = xMalloc (1, tagEntryInfoX);
1069 	x->symtab = RB_ROOT;
1070 	x->corkIndex = CORK_NIL;
1071 	tagEntryInfo  *slot = (tagEntryInfo *)x;
1072 
1073 	*slot = *tag;
1074 
1075 	if (slot->pattern)
1076 		slot->pattern = eStrdup (slot->pattern);
1077 
1078 	slot->inputFileName = eStrdup (slot->inputFileName);
1079 	slot->name = eStrdup (slot->name);
1080 	if (slot->extensionFields.access)
1081 		slot->extensionFields.access = eStrdup (slot->extensionFields.access);
1082 	if (slot->extensionFields.implementation)
1083 		slot->extensionFields.implementation = eStrdup (slot->extensionFields.implementation);
1084 	if (slot->extensionFields.inheritance)
1085 		slot->extensionFields.inheritance = eStrdup (slot->extensionFields.inheritance);
1086 	if (slot->extensionFields.scopeName)
1087 		slot->extensionFields.scopeName = eStrdup (slot->extensionFields.scopeName);
1088 	if (slot->extensionFields.signature)
1089 		slot->extensionFields.signature = eStrdup (slot->extensionFields.signature);
1090 	if (slot->extensionFields.typeRef[0])
1091 		slot->extensionFields.typeRef[0] = eStrdup (slot->extensionFields.typeRef[0]);
1092 	if (slot->extensionFields.typeRef[1])
1093 		slot->extensionFields.typeRef[1] = eStrdup (slot->extensionFields.typeRef[1]);
1094 #ifdef HAVE_LIBXML
1095 	if (slot->extensionFields.xpath)
1096 		slot->extensionFields.xpath = eStrdup (slot->extensionFields.xpath);
1097 #endif
1098 
1099 	if (slot->extraDynamic)
1100 	{
1101 		int n = countXtags () - XTAG_COUNT;
1102 		slot->extraDynamic = xCalloc ((n / 8) + 1, uint8_t);
1103 		memcpy (slot->extraDynamic, tag->extraDynamic, (n / 8) + 1);
1104 	}
1105 
1106 	if (slot->sourceFileName)
1107 		slot->sourceFileName = eStrdup (slot->sourceFileName);
1108 
1109 
1110 	slot->usedParserFields = 0;
1111 	slot->parserFieldsDynamic = NULL;
1112 	copyParserFields (tag, slot);
1113 	if (slot->parserFieldsDynamic)
1114 		PARSER_TRASH_BOX_TAKE_BACK(slot->parserFieldsDynamic);
1115 
1116 	return x;
1117 }
1118 
clearParserFields(tagEntryInfo * const tag)1119 static void clearParserFields (tagEntryInfo *const tag)
1120 {
1121 	unsigned int i, n;
1122 	const char* value;
1123 
1124 	if ( tag->usedParserFields < PRE_ALLOCATED_PARSER_FIELDS )
1125 		n = tag->usedParserFields;
1126 	else
1127 		n = PRE_ALLOCATED_PARSER_FIELDS;
1128 
1129 	for (i = 0; i < n; i++)
1130 	{
1131 		value = tag->parserFields[i].value;
1132 		if (value && tag->parserFields[i].valueOwner)
1133 			eFree ((char *)value);
1134 		tag->parserFields[i].value = NULL;
1135 		tag->parserFields[i].ftype = FIELD_UNKNOWN;
1136 	}
1137 	if (tag->parserFieldsDynamic)
1138 	{
1139 		ptrArrayDelete (tag->parserFieldsDynamic);
1140 		tag->parserFieldsDynamic = NULL;
1141 	}
1142 }
1143 
deleteTagEnry(void * data)1144 static void deleteTagEnry (void *data)
1145 {
1146 	tagEntryInfo *slot = data;
1147 
1148 	if (slot->kindIndex == KIND_FILE_INDEX)
1149 		goto out;
1150 
1151 	if (slot->pattern)
1152 		eFree ((char *)slot->pattern);
1153 	eFree ((char *)slot->inputFileName);
1154 	eFree ((char *)slot->name);
1155 
1156 	if (slot->extensionFields.access)
1157 		eFree ((char *)slot->extensionFields.access);
1158 	if (slot->extensionFields.implementation)
1159 		eFree ((char *)slot->extensionFields.implementation);
1160 	if (slot->extensionFields.inheritance)
1161 		eFree ((char *)slot->extensionFields.inheritance);
1162 	if (slot->extensionFields.scopeName)
1163 		eFree ((char *)slot->extensionFields.scopeName);
1164 	if (slot->extensionFields.signature)
1165 		eFree ((char *)slot->extensionFields.signature);
1166 	if (slot->extensionFields.typeRef[0])
1167 		eFree ((char *)slot->extensionFields.typeRef[0]);
1168 	if (slot->extensionFields.typeRef[1])
1169 		eFree ((char *)slot->extensionFields.typeRef[1]);
1170 #ifdef HAVE_LIBXML
1171 	if (slot->extensionFields.xpath)
1172 		eFree ((char *)slot->extensionFields.xpath);
1173 #endif
1174 
1175 	if (slot->extraDynamic)
1176 		eFree (slot->extraDynamic);
1177 
1178 	if (slot->sourceFileName)
1179 		eFree ((char *)slot->sourceFileName);
1180 
1181 	clearParserFields (slot);
1182 
1183  out:
1184 	eFree (slot);
1185 }
1186 
corkSymtabPut(tagEntryInfoX * scope,const char * name,tagEntryInfoX * item)1187 static void corkSymtabPut (tagEntryInfoX *scope, const char* name, tagEntryInfoX *item)
1188 {
1189 	struct rb_root *root = &scope->symtab;
1190 
1191 	struct rb_node **new = &(root->rb_node), *parent = NULL;
1192 
1193 	while (*new)
1194 	{
1195 		tagEntryInfoX *this = container_of(*new, tagEntryInfoX, symnode);
1196 		int result = strcmp(item->slot.name, this->slot.name);
1197 
1198 		parent = *new;
1199 
1200 		if (result < 0)
1201 			new = &((*new)->rb_left);
1202 		else if (result > 0)
1203 			new = &((*new)->rb_right);
1204 		else
1205 		{
1206 			unsigned long lthis = this->slot.lineNumber;
1207 			unsigned long litem = item->slot.lineNumber;
1208 
1209 			/* Comparing lineNumber */
1210 			if (litem < lthis)
1211 				new = &((*new)->rb_left);
1212 			else if (litem > lthis)
1213 				new = &((*new)->rb_right);
1214 			else
1215 			{
1216 				/* Comparing memory address */
1217 				if (item < this)
1218 					new = &((*new)->rb_left);
1219 				else if (item > this)
1220 					new = &((*new)->rb_right);
1221 				else
1222 				{
1223 					AssertNotReached(); /* registering the same object twice. */
1224 					return;
1225 				}
1226 			}
1227 		}
1228 	}
1229 
1230 	verbose ("symtbl[:=] %s<-%s/%p (line: %lu)\n",
1231 			*new? container_of(*new, tagEntryInfoX, symnode)->slot.name: "*root*",
1232 			 item->slot.name, &item->slot, item->slot.lineNumber);
1233 	/* Add new node and rebalance tree. */
1234 	rb_link_node(&item->symnode, parent, new);
1235 	rb_insert_color(&item->symnode, root);
1236 }
1237 
corkSymtabUnlink(tagEntryInfoX * scope,tagEntryInfoX * item)1238 static void corkSymtabUnlink (tagEntryInfoX *scope, tagEntryInfoX *item)
1239 {
1240 	struct rb_root *root = &scope->symtab;
1241 	rb_erase (&item->symnode, root);
1242 }
1243 
foreachEntriesInScope(int corkIndex,const char * name,entryForeachFunc func,void * data)1244 extern bool foreachEntriesInScope (int corkIndex,
1245 								   const char *name,
1246 								   entryForeachFunc func,
1247 								   void *data)
1248 {
1249 	tagEntryInfoX *x = ptrArrayItem (TagFile.corkQueue, corkIndex);
1250 
1251 	struct rb_root *root = &x->symtab;
1252 	tagEntryInfoX *rep = NULL;
1253 
1254 	/* More than one tag can have a same name.
1255 	 * Visit them from the last.
1256 	 *
1257 	 * 1. find one of them as the representative,
1258 	 * 2. find the last one of them from the representative with rb_next,
1259 	 * 3. call FUNC iteratively from the last to the first.
1260 	 */
1261 	if (name)
1262 	{
1263 		struct rb_node *node = root->rb_node;
1264 		while (node)
1265 		{
1266 			tagEntryInfoX *entry = container_of(node, tagEntryInfoX, symnode);
1267 			int result;
1268 
1269 			result = strcmp(name, entry->slot.name);
1270 
1271 			if (result < 0)
1272 				node = node->rb_left;
1273 			else if (result > 0)
1274 				node = node->rb_right;
1275 			else
1276 			{
1277 				rep = entry;
1278 				break;
1279 			}
1280 		}
1281 		if (rep == NULL)
1282 			return true;
1283 
1284 		verbose("symtbl[<>] %s->%p\n", name, &rep->slot);
1285 	}
1286 
1287 	struct rb_node *last;
1288 
1289 	if (name)
1290 	{
1291 		struct rb_node *tmp = &rep->symnode;
1292 		last = tmp;
1293 
1294 		while ((tmp = rb_next (tmp)))
1295 		{
1296 			tagEntryInfoX *entry = container_of(tmp, tagEntryInfoX, symnode);
1297 			if (strcmp(name, entry->slot.name) == 0)
1298 			{
1299 				verbose ("symtbl[ >] %s->%p\n", name, &container_of(tmp, tagEntryInfoX, symnode)->slot);
1300 				last = tmp;
1301 			}
1302 			else
1303 				break;
1304 		}
1305 	}
1306 	else
1307 	{
1308 		last = rb_last(root);
1309 		verbose ("last for %d<%p>: %p\n", corkIndex, root, last);
1310 	}
1311 
1312 	if (!last)
1313 	{
1314 		verbose ("symtbl[>V] %s->%p\n", name? name: "(null)", NULL);
1315 		return true;			/* Nothing here in this node. */
1316 	}
1317 
1318 	verbose ("symtbl[>|] %s->%p\n", name, &container_of(last, tagEntryInfoX, symnode)->slot);
1319 
1320 	struct rb_node *cursor = last;
1321 	bool revisited_rep = false;
1322 	do
1323 	{
1324 		tagEntryInfoX *entry = container_of(cursor, tagEntryInfoX, symnode);
1325 		if (!revisited_rep || !name || !strcmp(name, entry->slot.name))
1326 		{
1327 			verbose ("symtbl[< ] %s->%p\n", name, &entry->slot);
1328 			if (!func (entry->corkIndex, &entry->slot, data))
1329 				return false;
1330 			if (cursor == &rep->symnode)
1331 				revisited_rep = true;
1332 		}
1333 		else if (name)
1334 			break;
1335 	}
1336 	while ((cursor = rb_prev(cursor)));
1337 
1338 	return true;
1339 }
1340 
1341 struct anyEntryInScopeData {
1342 	int index;
1343 	bool onlyDefinitionTag;
1344 };
1345 
findName(int corkIndex,tagEntryInfo * entry,void * cbData)1346 static bool findName (int corkIndex, tagEntryInfo *entry, void *cbData)
1347 {
1348 	struct anyEntryInScopeData *data = cbData;
1349 
1350 	if (data->onlyDefinitionTag && !isRoleAssigned (entry, ROLE_DEFINITION_INDEX))
1351 		return true;
1352 
1353 	data->index = corkIndex;
1354 	return false;
1355 }
1356 
anyEntryInScope(int corkIndex,const char * name,bool onlyDefinitionTag)1357 int anyEntryInScope (int corkIndex, const char *name, bool onlyDefinitionTag)
1358 {
1359 	struct anyEntryInScopeData data = {
1360 		.index = CORK_NIL,
1361 		.onlyDefinitionTag = onlyDefinitionTag,
1362 	};
1363 
1364 	if (foreachEntriesInScope (corkIndex, name, findName, &data) == false)
1365 		return data.index;
1366 
1367 	return CORK_NIL;
1368 }
1369 
1370 struct anyKindsEntryInScopeData {
1371 	int  index;
1372 	const int *kinds;
1373 	int  count;
1374 	bool onlyDefinitionTag;
1375 };
1376 
findNameOfKinds(int corkIndex,tagEntryInfo * entry,void * data)1377 static bool findNameOfKinds (int corkIndex, tagEntryInfo *entry, void *data)
1378 {
1379 	struct anyKindsEntryInScopeData * kdata = data;
1380 
1381 	for (int i = 0; i < kdata->count; i++)
1382 	{
1383 		int k = kdata->kinds [i];
1384 		if (entry->kindIndex == k
1385 			&& ((!kdata->onlyDefinitionTag)
1386 				|| isRoleAssigned (entry, ROLE_DEFINITION_INDEX)))
1387 		{
1388 			kdata->index = corkIndex;
1389 			return false;
1390 		}
1391 	}
1392 	return true;
1393 }
1394 
anyKindEntryInScope(int corkIndex,const char * name,int kind,bool onlyDefinitionTag)1395 int anyKindEntryInScope (int corkIndex,
1396 						 const char *name, int kind,
1397 						 bool onlyDefinitionTag)
1398 {
1399 	return anyKindsEntryInScope (corkIndex, name, &kind, 1, onlyDefinitionTag);
1400 }
1401 
anyKindsEntryInScope(int corkIndex,const char * name,const int * kinds,int count,bool onlyDefinitionTag)1402 int anyKindsEntryInScope (int corkIndex,
1403 						  const char *name,
1404 						  const int *kinds, int count,
1405 						  bool onlyDefinitionTag)
1406 {
1407 	struct anyKindsEntryInScopeData data = {
1408 		.index = CORK_NIL,
1409 		.kinds = kinds,
1410 		.count = count,
1411 		.onlyDefinitionTag = onlyDefinitionTag,
1412 	};
1413 
1414 	if (foreachEntriesInScope (corkIndex, name, findNameOfKinds, &data) == false)
1415 		return data.index;
1416 
1417 	return CORK_NIL;
1418 }
1419 
anyKindsEntryInScopeRecursive(int corkIndex,const char * name,const int * kinds,int count,bool onlyDefinitionTag)1420 int anyKindsEntryInScopeRecursive (int corkIndex,
1421 								   const char *name,
1422 								   const int *kinds, int count,
1423 								   bool onlyDefinitionTag)
1424 {
1425 	struct anyKindsEntryInScopeData data = {
1426 		.index = CORK_NIL,
1427 		.kinds = kinds,
1428 		.count = count,
1429 		.onlyDefinitionTag = onlyDefinitionTag,
1430 	};
1431 
1432 	tagEntryInfo *e;
1433 	do
1434 	{
1435 		if (foreachEntriesInScope (corkIndex, name, findNameOfKinds, &data) == false)
1436 			return data.index;
1437 
1438 		if (corkIndex == CORK_NIL)
1439 			break;
1440 
1441 		e = getEntryInCorkQueue (corkIndex);
1442 		if (!e)
1443 			break;
1444 		corkIndex = e->extensionFields.scopeIndex;
1445 	}
1446 	while (1);
1447 
1448 	return CORK_NIL;
1449 }
1450 
registerEntry(int corkIndex)1451 extern void registerEntry (int corkIndex)
1452 {
1453 	Assert (TagFile.corkFlags & CORK_SYMTAB);
1454 	Assert (corkIndex != CORK_NIL);
1455 
1456 	tagEntryInfoX *e = ptrArrayItem (TagFile.corkQueue, corkIndex);
1457 	{
1458 		tagEntryInfoX *scope = ptrArrayItem (TagFile.corkQueue, e->slot.extensionFields.scopeIndex);
1459 		corkSymtabPut (scope, e->slot.name, e);
1460 	}
1461 }
1462 
unregisterEntry(int corkIndex)1463 extern void unregisterEntry(int corkIndex)
1464 {
1465 	Assert (TagFile.corkFlags & CORK_SYMTAB);
1466 	Assert (corkIndex != CORK_NIL);
1467 
1468 	tagEntryInfoX *e = ptrArrayItem (TagFile.corkQueue, corkIndex);
1469 	{
1470 		tagEntryInfoX *scope = ptrArrayItem (TagFile.corkQueue, e->slot.extensionFields.scopeIndex);
1471 		corkSymtabUnlink (scope, e);
1472 	}
1473 
1474 }
1475 
queueTagEntry(const tagEntryInfo * const tag)1476 static int queueTagEntry(const tagEntryInfo *const tag)
1477 {
1478 	static bool warned;
1479 
1480 	int corkIndex;
1481 	tagEntryInfoX * entry = copyTagEntry (tag,
1482 										TagFile.corkFlags);
1483 
1484 	if (ptrArrayCount (TagFile.corkQueue) == (size_t)INT_MAX)
1485 	{
1486 		if (!warned)
1487 		{
1488 			warned = true;
1489 			error (WARNING,
1490 				   "The tag entry queue overflows; drop the tag entry at %lu in %s",
1491 				   tag->lineNumber,
1492 				   tag->inputFileName);
1493 		}
1494 		return CORK_NIL;
1495 	}
1496 	warned = false;
1497 
1498 	corkIndex = (int)ptrArrayAdd (TagFile.corkQueue, entry);
1499 	entry->corkIndex = corkIndex;
1500 	entry->slot.inCorkQueue = 1;
1501 
1502 	return corkIndex;
1503 }
1504 
setupWriter(void * writerClientData)1505 extern void setupWriter (void *writerClientData)
1506 {
1507 	writerSetup (TagFile.mio, writerClientData);
1508 }
1509 
teardownWriter(const char * filename)1510 extern bool teardownWriter (const char *filename)
1511 {
1512 	return writerTeardown (TagFile.mio, filename);
1513 }
1514 
isTagWritable(const tagEntryInfo * const tag)1515 static bool isTagWritable(const tagEntryInfo *const tag)
1516 {
1517 	if (tag->placeholder)
1518 		return false;
1519 
1520 	if (! isLanguageKindEnabled(tag->langType, tag->kindIndex))
1521 		return false;
1522 
1523 	if (tag->extensionFields.roleBits)
1524 	{
1525 		size_t available_roles;
1526 
1527 		if (!isXtagEnabled (XTAG_REFERENCE_TAGS))
1528 			return false;
1529 
1530 		available_roles = countLanguageRoles(tag->langType,
1531 											 tag->kindIndex);
1532 		if (tag->extensionFields.roleBits >=
1533 			(makeRoleBit(available_roles)))
1534 			return false;
1535 
1536 		/* TODO: optimization
1537 		   A Bitmasks representing all enabled roles can be calculated at the
1538 		   end of initializing the parser. Calculating each time when checking
1539 		   a tag entry is not needed. */
1540 		for (unsigned int roleIndex = 0; roleIndex < available_roles; roleIndex++)
1541 		{
1542 			if (isRoleAssigned(tag, roleIndex))
1543 			{
1544 				if (isLanguageRoleEnabled (tag->langType, tag->kindIndex,
1545 											 roleIndex))
1546 					return true;
1547 			}
1548 
1549 		}
1550 		return false;
1551 	}
1552 	else if (isLanguageKindRefOnly(tag->langType, tag->kindIndex))
1553 	{
1554 		error (WARNING, "PARSER BUG: a definition tag for a refonly kind(%s.%s) of is made: %s found in %s.",
1555 			   getLanguageName(tag->langType),
1556 			   getLanguageKind(tag->langType, tag->kindIndex)->name,
1557 			   tag->name, tag->inputFileName);
1558 		/* This one is not so critical. */
1559 	}
1560 
1561 	if (!isXtagEnabled(XTAG_ANONYMOUS)
1562 		&& isTagExtraBitMarked(tag, XTAG_ANONYMOUS))
1563 		return false;
1564 
1565 	return true;
1566 }
1567 
buildFqTagCache(tagEntryInfo * const tag)1568 static void buildFqTagCache (tagEntryInfo *const tag)
1569 {
1570 	getTagScopeInformation (tag, NULL, NULL);
1571 }
1572 
writeTagEntry(const tagEntryInfo * const tag)1573 static void writeTagEntry (const tagEntryInfo *const tag)
1574 {
1575 	int length = 0;
1576 
1577 	Assert (tag->kindIndex != KIND_GHOST_INDEX);
1578 
1579 	DebugStatement ( debugEntry (tag); )
1580 
1581 #ifdef WIN32
1582 	if (getFilenameSeparator(Option.useSlashAsFilenameSeparator) == FILENAME_SEP_USE_SLASH)
1583 	{
1584 		Assert (((const tagEntryInfo *)tag)->inputFileName);
1585 		char *c = (char *)(((tagEntryInfo *const)tag)->inputFileName);
1586 		while (*c)
1587 		{
1588 			if (*c == PATH_SEPARATOR)
1589 				*c = OUTPUT_PATH_SEPARATOR;
1590 			c++;
1591 		}
1592 	}
1593 #endif
1594 
1595 	if (includeExtensionFlags ()
1596 	    && isXtagEnabled (XTAG_QUALIFIED_TAGS)
1597 	    && doesInputLanguageRequestAutomaticFQTag (tag)
1598 		&& !isTagExtraBitMarked (tag, XTAG_QUALIFIED_TAGS)
1599 		&& !tag->skipAutoFQEmission)
1600 	{
1601 		/* const is discarded to update the cache field of TAG. */
1602 		buildFqTagCache ( (tagEntryInfo *const)tag);
1603 	}
1604 
1605 	length = writerWriteTag (TagFile.mio, tag);
1606 
1607 	if (length > 0)
1608 	{
1609 		++TagFile.numTags.added;
1610 		rememberMaxLengths (strlen (tag->name), (size_t) length);
1611 	}
1612 	DebugStatement ( if (TagFile.mio) mio_flush (TagFile.mio); )
1613 
1614 	abort_if_ferror (TagFile.mio);
1615 }
1616 
writePseudoTag(const ptagDesc * desc,const char * const fileName,const char * const pattern,const char * const parserName)1617 extern bool writePseudoTag (const ptagDesc *desc,
1618 			       const char *const fileName,
1619 			       const char *const pattern,
1620 			       const char *const parserName)
1621 {
1622 	int length;
1623 
1624 	length = writerWritePtag (TagFile.mio, desc, fileName,
1625 							  pattern, parserName);
1626 	if (length < 0)
1627 		return false;
1628 
1629 	abort_if_ferror (TagFile.mio);
1630 
1631 	++TagFile.numTags.added;
1632 	rememberMaxLengths (strlen (desc->name), (size_t) length);
1633 
1634 	return true;
1635 }
1636 
corkTagFile(unsigned int corkFlags)1637 extern void corkTagFile(unsigned int corkFlags)
1638 {
1639 	TagFile.cork++;
1640 	if (TagFile.cork == 1)
1641 	{
1642 		TagFile.corkFlags = corkFlags;
1643 		TagFile.corkQueue = ptrArrayNew (deleteTagEnry);
1644 		tagEntryInfo *nil = newNilTagEntry (corkFlags);
1645 		ptrArrayAdd (TagFile.corkQueue, nil);
1646 	}
1647 }
1648 
uncorkTagFile(void)1649 extern void uncorkTagFile(void)
1650 {
1651 	unsigned int i;
1652 
1653 	TagFile.cork--;
1654 
1655 	if (TagFile.cork > 0)
1656 		return ;
1657 
1658 	for (i = 1; i < ptrArrayCount (TagFile.corkQueue); i++)
1659 	{
1660 		tagEntryInfo *tag = ptrArrayItem (TagFile.corkQueue, i);
1661 
1662 		if (!isTagWritable(tag))
1663 			continue;
1664 
1665 		writeTagEntry (tag);
1666 
1667 		if (doesInputLanguageRequestAutomaticFQTag (tag)
1668 		    && isXtagEnabled (XTAG_QUALIFIED_TAGS)
1669 			&& !isTagExtraBitMarked (tag, XTAG_QUALIFIED_TAGS)
1670 			&& !tag->skipAutoFQEmission
1671 			&& ((tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX
1672 				 && tag->extensionFields.scopeName != NULL
1673 				 && tag->extensionFields.scopeIndex != CORK_NIL)
1674 				|| (tag->extensionFields.scopeKindIndex == KIND_GHOST_INDEX
1675 					&& tag->extensionFields.scopeName == NULL
1676 					&& tag->extensionFields.scopeIndex == CORK_NIL)))
1677 			makeQualifiedTagEntry (tag);
1678 	}
1679 
1680 	ptrArrayDelete (TagFile.corkQueue);
1681 	TagFile.corkQueue = NULL;
1682 }
1683 
getEntryInCorkQueue(int n)1684 extern tagEntryInfo *getEntryInCorkQueue   (int n)
1685 {
1686 	if ((CORK_NIL < n) && (((size_t)n) < ptrArrayCount (TagFile.corkQueue)))
1687 		return ptrArrayItem (TagFile.corkQueue, n);
1688 	else
1689 		return NULL;
1690 }
1691 
getEntryOfNestingLevel(const NestingLevel * nl)1692 extern tagEntryInfo *getEntryOfNestingLevel (const NestingLevel *nl)
1693 {
1694 	if (nl == NULL)
1695 		return NULL;
1696 	return getEntryInCorkQueue (nl->corkIndex);
1697 }
1698 
countEntryInCorkQueue(void)1699 extern size_t        countEntryInCorkQueue (void)
1700 {
1701 	return ptrArrayCount (TagFile.corkQueue);
1702 }
1703 
markTagPlaceholder(tagEntryInfo * e,bool placeholder)1704 extern void markTagPlaceholder (tagEntryInfo *e, bool placeholder)
1705 {
1706 	e->placeholder = placeholder;
1707 }
1708 
makePlaceholder(const char * const name)1709 extern int makePlaceholder (const char *const name)
1710 {
1711 	tagEntryInfo e;
1712 
1713 	initTagEntry (&e, name, KIND_GHOST_INDEX);
1714 	markTagPlaceholder(&e, true);
1715 
1716 	/*
1717 	 * makePlaceholder may be called even before reading any bytes
1718 	 * from the input stream. In such case, initTagEntry fills
1719 	 * the lineNumber field of the placeholder tag with 0.
1720 	 * This breaks an assertion in makeTagEntry. Following adjustment
1721 	 * is for avoiding it.
1722 	 */
1723 	if (e.lineNumber == 0)
1724 		e.lineNumber = 1;
1725 
1726 	return makeTagEntry (&e);
1727 }
1728 
makeTagEntry(const tagEntryInfo * const tag)1729 extern int makeTagEntry (const tagEntryInfo *const tag)
1730 {
1731 	int r = CORK_NIL;
1732 	Assert (tag->name != NULL);
1733 	Assert(tag->lineNumber > 0);
1734 
1735 	if (!TagFile.cork)
1736 		if (!isTagWritable (tag))
1737 			goto out;
1738 
1739 	if (tag->name [0] == '\0' && (!tag->placeholder))
1740 	{
1741 		if (!doesInputLanguageAllowNullTag())
1742 			error (WARNING, "ignoring null tag in %s(line: %lu)",
1743 			       getInputFileName (), tag->lineNumber);
1744 		goto out;
1745 	}
1746 
1747 	if (TagFile.cork)
1748 		r = queueTagEntry (tag);
1749 	else
1750 		writeTagEntry (tag);
1751 
1752 	if (r != CORK_NIL)
1753 		notifyMakeTagEntry (tag, r);
1754 
1755 out:
1756 	return r;
1757 }
1758 
makeQualifiedTagEntry(const tagEntryInfo * const e)1759 extern int makeQualifiedTagEntry (const tagEntryInfo *const e)
1760 {
1761 	int r = CORK_NIL;
1762 	tagEntryInfo x;
1763 	int xk;
1764 	const char *sep;
1765 	static vString *fqn;
1766 
1767 	if (isXtagEnabled (XTAG_QUALIFIED_TAGS))
1768 	{
1769 		x = *e;
1770 		markTagExtraBit (&x, XTAG_QUALIFIED_TAGS);
1771 
1772 		fqn = vStringNewOrClearWithAutoRelease (fqn);
1773 
1774 		if (e->extensionFields.scopeName)
1775 		{
1776 			vStringCatS (fqn, e->extensionFields.scopeName);
1777 			xk = e->extensionFields.scopeKindIndex;
1778 			sep = scopeSeparatorFor (e->langType, e->kindIndex, xk);
1779 			vStringCatS (fqn, sep);
1780 		}
1781 		else
1782 		{
1783 			/* This is an top level item. prepend a root separator
1784 			   if the kind of the item has. */
1785 			sep = scopeSeparatorFor (e->langType, e->kindIndex, KIND_GHOST_INDEX);
1786 			if (sep == NULL)
1787 			{
1788 				/* No root separator. The name of the
1789 				   optional tag and that of full qualified tag
1790 				   are the same; recording the full qualified tag
1791 				   is meaningless. */
1792 				return r;
1793 			}
1794 			else
1795 				vStringCatS (fqn, sep);
1796 		}
1797 		vStringCatS (fqn, e->name);
1798 
1799 		x.name = vStringValue (fqn);
1800 		/* makeExtraTagEntry of c.c doesn't clear scope
1801 		   related fields. */
1802 #if 0
1803 		x.extensionFields.scopeKind = NULL;
1804 		x.extensionFields.scopeName = NULL;
1805 		x.extensionFields.scopeIndex = CORK_NIL;
1806 #endif
1807 
1808 		bool in_subparser
1809 			= isTagExtraBitMarked (&x,
1810 								   XTAG_SUBPARSER);
1811 
1812 		if (in_subparser)
1813 			pushLanguage(x.langType);
1814 
1815 		r = makeTagEntry (&x);
1816 
1817 		if (in_subparser)
1818 			popLanguage();
1819 	}
1820 	return r;
1821 }
1822 
setTagPositionFromTag(tagEntryInfo * const dst,const tagEntryInfo * const src)1823 extern void setTagPositionFromTag (tagEntryInfo *const dst,
1824 								   const tagEntryInfo *const src)
1825 {
1826 		dst->lineNumber = src->lineNumber;
1827 		dst->boundaryInfo = src->boundaryInfo;
1828 		dst->filePosition = src->filePosition;
1829 }
1830 
initTagEntryFull(tagEntryInfo * const e,const char * const name,unsigned long lineNumber,langType langType_,MIOPos filePosition,const char * inputFileName,int kindIndex,roleBitsType roleBits,const char * sourceFileName,langType sourceLangType,long sourceLineNumberDifference)1831 static void initTagEntryFull (tagEntryInfo *const e, const char *const name,
1832 			      unsigned long lineNumber,
1833 			      langType langType_,
1834 			      MIOPos      filePosition,
1835 			      const char *inputFileName,
1836 			      int kindIndex,
1837 			      roleBitsType roleBits,
1838 			      const char *sourceFileName,
1839 			      langType sourceLangType,
1840 			      long sourceLineNumberDifference)
1841 {
1842 	int i;
1843 
1844 	Assert (getInputFileName() != NULL);
1845 
1846 	memset (e, 0, sizeof (tagEntryInfo));
1847 	e->lineNumberEntry = (bool) (Option.locate == EX_LINENUM);
1848 	e->lineNumber      = lineNumber;
1849 	e->boundaryInfo    = getNestedInputBoundaryInfo (lineNumber);
1850 	e->langType        = langType_;
1851 	e->filePosition    = filePosition;
1852 	e->inputFileName   = inputFileName;
1853 	e->name            = name;
1854 	e->extensionFields.scopeLangType = LANG_AUTO;
1855 	e->extensionFields.scopeKindIndex = KIND_GHOST_INDEX;
1856 	e->extensionFields.scopeIndex     = CORK_NIL;
1857 
1858 	Assert (kindIndex < 0 || kindIndex < (int)countLanguageKinds(langType_));
1859 	e->kindIndex = kindIndex;
1860 
1861 	Assert (roleBits == 0
1862 			|| (roleBits < (makeRoleBit(countLanguageRoles(langType_, kindIndex)))));
1863 	e->extensionFields.roleBits = roleBits;
1864 	if (roleBits)
1865 		markTagExtraBit (e, XTAG_REFERENCE_TAGS);
1866 
1867 	e->extensionFields.nth = NO_NTH_FIELD;
1868 
1869 	if (doesParserRunAsGuest ())
1870 		markTagExtraBit (e, XTAG_GUEST);
1871 	if (doesSubparserRun ())
1872 		markTagExtraBit (e, XTAG_SUBPARSER);
1873 
1874 	e->sourceLangType = sourceLangType;
1875 	e->sourceFileName = sourceFileName;
1876 	e->sourceLineNumberDifference = sourceLineNumberDifference;
1877 
1878 	e->usedParserFields = 0;
1879 
1880 	for ( i = 0; i < PRE_ALLOCATED_PARSER_FIELDS; i++ )
1881 		e->parserFields[i].ftype = FIELD_UNKNOWN;
1882 
1883 	if (isParserMarkedNoEmission ())
1884 		e->placeholder = 1;
1885 }
1886 
initTagEntry(tagEntryInfo * const e,const char * const name,int kindIndex)1887 extern void initTagEntry (tagEntryInfo *const e, const char *const name,
1888 						  int kindIndex)
1889 {
1890 	initTagEntryFull(e, name,
1891 			 getInputLineNumber (),
1892 			 getInputLanguage (),
1893 			 getInputFilePosition (),
1894 			 getInputFileTagPath (),
1895 			 kindIndex,
1896 			 0,
1897 			 getSourceFileTagPath(),
1898 			 getSourceLanguage(),
1899 			 getSourceLineNumber() - getInputLineNumber ());
1900 }
1901 
initRefTagEntry(tagEntryInfo * const e,const char * const name,int kindIndex,int roleIndex)1902 extern void initRefTagEntry (tagEntryInfo *const e, const char *const name,
1903 			     int kindIndex, int roleIndex)
1904 {
1905 	initForeignRefTagEntry (e, name, getInputLanguage (), kindIndex, roleIndex);
1906 }
1907 
initForeignTagEntry(tagEntryInfo * const e,const char * const name,langType type,int kindIndex)1908 extern void initForeignTagEntry (tagEntryInfo *const e, const char *const name,
1909 								 langType type,
1910 								 int kindIndex)
1911 {
1912 	initForeignRefTagEntry (e, name, type, kindIndex, ROLE_DEFINITION_INDEX);
1913 }
1914 
initForeignRefTagEntry(tagEntryInfo * const e,const char * const name,langType langType,int kindIndex,int roleIndex)1915 extern void initForeignRefTagEntry (tagEntryInfo *const e, const char *const name,
1916 									langType langType,
1917 									int kindIndex, int roleIndex)
1918 {
1919 	initTagEntryFull(e, name,
1920 			 getInputLineNumber (),
1921 			 langType,
1922 			 getInputFilePosition (),
1923 			 getInputFileTagPath (),
1924 			 kindIndex,
1925 			 makeRoleBit(roleIndex),
1926 			 getSourceFileTagPath(),
1927 			 getSourceLanguage(),
1928 			 getSourceLineNumber() - getInputLineNumber ());
1929 }
1930 
markTagExtraBitFull(tagEntryInfo * const tag,xtagType extra,bool mark)1931 static void    markTagExtraBitFull     (tagEntryInfo *const tag, xtagType extra, bool mark)
1932 {
1933 	unsigned int index;
1934 	unsigned int offset;
1935 	uint8_t *slot;
1936 
1937 	Assert (extra != XTAG_UNKNOWN);
1938 
1939 	if (extra < XTAG_COUNT)
1940 	{
1941 		index = (extra / 8);
1942 		offset = (extra % 8);
1943 		slot = tag->extra;
1944 	}
1945 	else if (tag->extraDynamic)
1946 	{
1947 		Assert (extra < countXtags ());
1948 
1949 		index = ((extra - XTAG_COUNT) / 8);
1950 		offset = ((extra - XTAG_COUNT) % 8);
1951 		slot = tag->extraDynamic;
1952 	}
1953 	else
1954 	{
1955 		Assert (extra < countXtags ());
1956 
1957 		int n = countXtags () - XTAG_COUNT;
1958 		tag->extraDynamic = xCalloc ((n / 8) + 1, uint8_t);
1959 		if (!tag->inCorkQueue)
1960 			PARSER_TRASH_BOX(tag->extraDynamic, eFree);
1961 		markTagExtraBitFull (tag, extra, mark);
1962 		return;
1963 	}
1964 
1965 	if (mark)
1966 		slot [ index ] |= (1 << offset);
1967 	else
1968 		slot [ index ] &= ~(1 << offset);
1969 }
1970 
markTagExtraBit(tagEntryInfo * const tag,xtagType extra)1971 extern void    markTagExtraBit     (tagEntryInfo *const tag, xtagType extra)
1972 {
1973 	markTagExtraBitFull (tag, extra, true);
1974 }
1975 
unmarkTagExtraBit(tagEntryInfo * const tag,xtagType extra)1976 extern void    unmarkTagExtraBit    (tagEntryInfo *const tag, xtagType extra)
1977 {
1978 	markTagExtraBitFull (tag, extra, false);
1979 }
1980 
isTagExtraBitMarked(const tagEntryInfo * const tag,xtagType extra)1981 extern bool isTagExtraBitMarked (const tagEntryInfo *const tag, xtagType extra)
1982 {
1983 	unsigned int index;
1984 	unsigned int offset;
1985 	const uint8_t *slot;
1986 
1987 	Assert (extra != XTAG_UNKNOWN);
1988 
1989 	if (extra < XTAG_COUNT)
1990 	{
1991 		index = (extra / 8);
1992 		offset = (extra % 8);
1993 		slot = tag->extra;
1994 	}
1995 	else if (!tag->extraDynamic)
1996 		return false;
1997 	else
1998 	{
1999 		Assert (extra < countXtags ());
2000 		index = ((extra - XTAG_COUNT) / 8);
2001 		offset = ((extra - XTAG_COUNT) % 8);
2002 		slot = tag->extraDynamic;
2003 	}
2004 	return !! ((slot [ index ]) & (1 << offset));
2005 }
2006 
isTagExtra(const tagEntryInfo * const tag)2007 extern bool isTagExtra (const tagEntryInfo *const tag)
2008 {
2009 	for (unsigned int i = 0; i < countXtags(); i++)
2010 		if (isTagExtraBitMarked (tag, i))
2011 			return true;
2012 	return false;
2013 }
2014 
assignRoleFull(tagEntryInfo * const e,int roleIndex,bool assign)2015 static void assignRoleFull(tagEntryInfo *const e, int roleIndex, bool assign)
2016 {
2017 	if (roleIndex == ROLE_DEFINITION_INDEX)
2018 	{
2019 		if (assign)
2020 		{
2021 			e->extensionFields.roleBits = 0;
2022 			markTagExtraBitFull (e, XTAG_REFERENCE_TAGS, false);
2023 		}
2024 	}
2025 	else if (roleIndex > ROLE_DEFINITION_INDEX)
2026 	{
2027 		Assert (roleIndex < (int)countLanguageRoles(e->langType, e->kindIndex));
2028 
2029 		if (assign)
2030 			e->extensionFields.roleBits |= (makeRoleBit(roleIndex));
2031 		else
2032 			e->extensionFields.roleBits &= ~(makeRoleBit(roleIndex));
2033 		markTagExtraBitFull (e, XTAG_REFERENCE_TAGS, e->extensionFields.roleBits);
2034 	}
2035 	else
2036 		AssertNotReached();
2037 }
2038 
assignRole(tagEntryInfo * const e,int roleIndex)2039 extern void assignRole(tagEntryInfo *const e, int roleIndex)
2040 {
2041 	assignRoleFull(e, roleIndex, true);
2042 }
2043 
isRoleAssigned(const tagEntryInfo * const e,int roleIndex)2044 extern bool isRoleAssigned(const tagEntryInfo *const e, int roleIndex)
2045 {
2046 	if (roleIndex == ROLE_DEFINITION_INDEX)
2047 		return (!e->extensionFields.roleBits);
2048 	else
2049 		return (e->extensionFields.roleBits & makeRoleBit(roleIndex));
2050 }
2051 
numTagsAdded(void)2052 extern unsigned long numTagsAdded(void)
2053 {
2054 	return TagFile.numTags.added;
2055 }
2056 
setNumTagsAdded(unsigned long nadded)2057 extern void setNumTagsAdded (unsigned long nadded)
2058 {
2059 	TagFile.numTags.added = nadded;
2060 }
2061 
numTagsTotal(void)2062 extern unsigned long numTagsTotal(void)
2063 {
2064 	return TagFile.numTags.added + TagFile.numTags.prev;
2065 }
2066 
maxTagsLine(void)2067 extern unsigned long maxTagsLine (void)
2068 {
2069 	return (unsigned long)TagFile.max.line;
2070 }
2071 
invalidatePatternCache(void)2072 extern void invalidatePatternCache(void)
2073 {
2074 	TagFile.patternCacheValid = false;
2075 }
2076 
tagFilePosition(MIOPos * p)2077 extern void tagFilePosition (MIOPos *p)
2078 {
2079 	/* mini-geany doesn't set TagFile.mio. */
2080 	if 	(TagFile.mio == NULL)
2081 		return;
2082 
2083 	if (mio_getpos (TagFile.mio, p) == -1)
2084 		error (FATAL|PERROR,
2085 			   "failed to get file position of the tag file\n");
2086 }
2087 
setTagFilePosition(MIOPos * p,bool truncation)2088 extern void setTagFilePosition (MIOPos *p, bool truncation)
2089 {
2090 	/* mini-geany doesn't set TagFile.mio. */
2091 	if 	(TagFile.mio == NULL)
2092 		return;
2093 
2094 
2095 	long t0 = 0;
2096 	if (truncation)
2097 		t0 = mio_tell (TagFile.mio);
2098 
2099 	if (mio_setpos (TagFile.mio, p) == -1)
2100 		error (FATAL|PERROR,
2101 			   "failed to set file position of the tag file\n");
2102 
2103 	if (truncation)
2104 	{
2105 		long t1 = mio_tell (TagFile.mio);
2106 		if (!mio_try_resize (TagFile.mio, (size_t)t1))
2107 			error (FATAL|PERROR,
2108 				   "failed to truncate the tag file %ld -> %ld\n", t0, t1);
2109 	}
2110 }
2111 
getTagFileDirectory(void)2112 extern const char* getTagFileDirectory (void)
2113 {
2114 	return TagFile.directory;
2115 }
2116 
markAsPlaceholder(int index,tagEntryInfo * e,void * data CTAGS_ATTR_UNUSED)2117 static bool markAsPlaceholder  (int index, tagEntryInfo *e, void *data CTAGS_ATTR_UNUSED)
2118 {
2119 	e->placeholder = 1;
2120 	markAllEntriesInScopeAsPlaceholder (index);
2121 	return true;
2122 }
2123 
markAllEntriesInScopeAsPlaceholder(int index)2124 extern void markAllEntriesInScopeAsPlaceholder (int index)
2125 {
2126 	foreachEntriesInScope (index, NULL, markAsPlaceholder, NULL);
2127 }
2128