xref: /Universal-ctags/main/writer-etags.c (revision d5406d79490b3ee2dfcbe86d9609fdbe60175ceb)
1 /*
2 *   Copyright (c) 1998-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 *   External interface to entry.c
8 */
9 
10 #include "general.h"  /* must always come first */
11 
12 #include <string.h>
13 
14 #include "debug.h"
15 #include "entry_p.h"
16 #include "mio.h"
17 #include "options_p.h"
18 #include "parse.h"
19 #include "parse_p.h"
20 #include "read.h"
21 #include "routines.h"
22 #include "routines_p.h"
23 #include "vstring.h"
24 #include "writer_p.h"
25 
26 
27 #define ETAGS_FILE  "TAGS"
28 
29 
30 static int writeEtagsEntry  (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag,
31 							 void *clientData CTAGS_ATTR_UNUSED);
32 static void *beginEtagsFile (tagWriter *writer, MIO * mio,
33 							 void *clientData CTAGS_ATTR_UNUSED);
34 static bool  endEtagsFile   (tagWriter *writer, MIO * mio, const char* filename,
35 							 void *clientData CTAGS_ATTR_UNUSED);
36 
37 tagWriter etagsWriter = {
38 	.writeEntry = writeEtagsEntry,
39 	.writePtagEntry = NULL,
40 	.preWriteEntry = beginEtagsFile,
41 	.postWriteEntry = endEtagsFile,
42 	.rescanFailedEntry = NULL,
43 	.treatFieldAsFixed = NULL,
44 	.defaultFileName = ETAGS_FILE,
45 };
46 
47 struct sEtags {
48 	char *name;
49 	MIO *mio;
50 	size_t byteCount;
51 	vString *vLine;
52 };
53 
54 
55 
beginEtagsFile(tagWriter * writer CTAGS_ATTR_UNUSED,MIO * mio CTAGS_ATTR_UNUSED,void * clientData CTAGS_ATTR_UNUSED)56 static void *beginEtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO *mio CTAGS_ATTR_UNUSED,
57 							 void *clientData CTAGS_ATTR_UNUSED)
58 {
59 	static struct sEtags etags = { NULL, NULL, 0, NULL };
60 
61 	etags.mio = tempFile ("w+b", &etags.name);
62 	etags.byteCount = 0;
63 	etags.vLine = vStringNew ();
64 	return &etags;
65 }
66 
endEtagsFile(tagWriter * writer,MIO * mainfp,const char * filename,void * clientData CTAGS_ATTR_UNUSED)67 static bool endEtagsFile (tagWriter *writer,
68 						  MIO *mainfp, const char *filename,
69 						  void *clientData CTAGS_ATTR_UNUSED)
70 {
71 	const char *line;
72 	struct sEtags *etags = writer->private;
73 
74 	mio_printf (mainfp, "\f\n%s,%ld\n", filename, (long) etags->byteCount);
75 	setNumTagsAdded (numTagsAdded () + 1);
76 	abort_if_ferror (mainfp);
77 
78 	if (etags->mio != NULL)
79 	{
80 		mio_rewind (etags->mio);
81 
82 		while ((line = readLineRaw (etags->vLine, etags->mio)) != NULL)
83 			mio_puts (mainfp, line);
84 
85 		vStringDelete (etags->vLine);
86 		mio_unref (etags->mio);
87 		remove (etags->name);
88 		eFree (etags->name);
89 		etags->vLine = NULL;
90 		etags->mio = NULL;
91 		etags->name = NULL;
92 	}
93 	return false;
94 }
95 
ada_suffix(const tagEntryInfo * const tag,const char * const line)96 static const char* ada_suffix (const tagEntryInfo *const tag, const char *const line)
97 {
98 	kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex);
99 
100 	Assert (kdef);
101 
102 	/* Mapping from ctags' kind letter to etags's suffix string.
103 	 * See https://www.gnu.org/software/emacs/manual/html_node/emacs/Tag-Syntax.html */
104 	switch (kdef->letter)
105 	{
106 	case 'p':
107 	case 'k':
108 		return "/b";
109 	case 'K':
110 		return "/k";
111 	case 'P':
112 		return "/s";
113 	case 't':
114 		return "/t";
115 	case 'R':
116 	case 'r':
117 	{
118 		/* Unlike etags, ctags uses the procedure kind for both
119 		 * procedures and functions. So in the level, emitting a tag,
120 		 * we cannot distinguish whether a tag is for a procedureor a
121 		 * function.
122 		 *
123 		 * If the typeref field of the tag is filled, we can say the tag
124 		 * is for a function. However, Ada parser doesn't implement the
125 		 * typeref field yet, and implementing it is not so easy.
126 		 *
127 		 * So we have to take an unclean way here: scanning the input
128 		 * line again.
129 		 * FIXME: remove the scanning code and implement the typeref field
130 		 * in Ada.
131 		 */
132 		const char *r = strstr (line, "return");
133 		const char *f = strstr (line, "function");
134 		const char *p = strstr (line, "procedure");
135 		if (r && f)
136 			return "/f";
137 		else if (p && !r)
138 			return "/p";
139 		return "";				/* Unknown */
140 	}
141 	default:
142 		return "";
143 	}
144 }
145 
writeEtagsEntry(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag,void * clientData CTAGS_ATTR_UNUSED)146 static int writeEtagsEntry (tagWriter *writer,
147 							MIO * mio, const tagEntryInfo *const tag,
148 							void *clientData CTAGS_ATTR_UNUSED)
149 {
150 	langType adaLangType = getNamedLanguage ("Ada", 0);
151 	Assert (adaLangType != LANG_IGNORE);
152 
153 	int length;
154 	struct sEtags *etags = writer->private;
155 
156 	mio = etags->mio;
157 
158 	if (tag->isFileEntry)
159 		length = mio_printf (mio, "\177%s\001%lu,0\n",
160 				tag->name, tag->lineNumber);
161 	else
162 	{
163 		size_t len;
164 		long seekValue;
165 		char *const line =
166 				readLineFromBypassForTag (etags->vLine, tag, &seekValue);
167 		if (line == NULL || line [0] == '\0')
168 			return 0;
169 
170 		len = strlen (line);
171 
172 		if (tag->truncateLineAfterTag)
173 			truncateTagLineAfterTag (line, tag->name, true);
174 		else if (line [len - 1] == '\n')
175 			line [--len] = '\0';
176 
177 		if (Option.patternLengthLimit > 0 && Option.patternLengthLimit < len)
178 		{
179 			unsigned int truncationLength = Option.patternLengthLimit;
180 
181 			/* don't cut in the middle of a UTF-8 character, but don't allow
182 			 * for more than one extra character in case it actually wasn't
183 			 * UTF-8.  See also entry.c:appendInputLine() */
184 			while (truncationLength < len &&
185 			       truncationLength < Option.patternLengthLimit + 3 &&
186 			       (((unsigned char) line[truncationLength]) & 0xc0) == 0x80)
187 				truncationLength++;
188 
189 			line [truncationLength] = '\0';
190 		}
191 
192 		length = mio_printf (mio, "%s\177%s%s\001%lu,%ld\n", line,
193 							 tag->name,
194 							 (tag->langType == adaLangType)
195 							 ? ada_suffix (tag, line)
196 							 : "",
197 							 tag->lineNumber, seekValue);
198 	}
199 	etags->byteCount += length;
200 
201 	return length;
202 }
203