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