xref: /Universal-ctags/main/writer-ctags.c (revision 0b491fdf2257a9d084b3b4e41485d7146b5c4381)
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 "entry_p.h"
13 #include "field.h"
14 #include "field_p.h"
15 #include "mio.h"
16 #include "options_p.h"
17 #include "parse_p.h"
18 #include "ptag_p.h"
19 #include "read.h"
20 #include "writer_p.h"
21 #include "xtag.h"
22 #include "xtag_p.h"
23 
24 
25 #define CTAGS_FILE  "tags"
26 
27 
28 static int writeCtagsEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
29 							MIO * mio, const tagEntryInfo *const tag,
30 							void *clientData);
31 static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
32 								MIO * mio, const ptagDesc *desc,
33 								const char *const fileName,
34 								const char *const pattern,
35 								const char *const parserName,
36 								void *clientData);
37 static bool treatFieldAsFixed (int fieldType);
38 static void checkCtagsOptions (tagWriter *writer, bool fieldsWereReset);
39 
40 #ifdef WIN32
41 static enum filenameSepOp overrideFilenameSeparator (enum filenameSepOp currentSetting);
42 #endif	/* WIN32 */
43 
44 struct rejection {
45 	bool rejectionInThisInput;
46 };
47 
48 tagWriter uCtagsWriter = {
49 	.writeEntry = writeCtagsEntry,
50 	.writePtagEntry = writeCtagsPtagEntry,
51 	.printPtagByDefault = true,
52 	.preWriteEntry = NULL,
53 	.postWriteEntry = NULL,
54 	.rescanFailedEntry = NULL,
55 	.treatFieldAsFixed = treatFieldAsFixed,
56 	.checkOptions = checkCtagsOptions,
57 #ifdef WIN32
58 	.overrideFilenameSeparator = overrideFilenameSeparator,
59 #endif
60 	.defaultFileName = CTAGS_FILE,
61 };
62 
beginECtagsFile(tagWriter * writer CTAGS_ATTR_UNUSED,MIO * mio CTAGS_ATTR_UNUSED,void * clientData CTAGS_ATTR_UNUSED)63 static void *beginECtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio CTAGS_ATTR_UNUSED,
64 							  void *clientData CTAGS_ATTR_UNUSED)
65 {
66 	static struct rejection rej;
67 
68 	rej.rejectionInThisInput = false;
69 
70 	return &rej;
71 }
72 
endECTagsFile(tagWriter * writer,MIO * mio CTAGS_ATTR_UNUSED,const char * filename CTAGS_ATTR_UNUSED,void * clientData CTAGS_ATTR_UNUSED)73 static bool endECTagsFile (tagWriter *writer, MIO * mio CTAGS_ATTR_UNUSED, const char* filename CTAGS_ATTR_UNUSED,
74 						   void *clientData CTAGS_ATTR_UNUSED)
75 {
76 	struct rejection *rej = writer->private;
77 	return rej->rejectionInThisInput;
78 }
79 
80 #ifdef WIN32
overrideFilenameSeparator(enum filenameSepOp currentSetting)81 static enum filenameSepOp overrideFilenameSeparator (enum filenameSepOp currentSetting)
82 {
83 	if (currentSetting == FILENAME_SEP_UNSET)
84 		return FILENAME_SEP_USE_SLASH;
85 	return currentSetting;
86 }
87 #endif
88 
89 tagWriter eCtagsWriter = {
90 	.writeEntry = writeCtagsEntry,
91 	.writePtagEntry = writeCtagsPtagEntry,
92 	.printPtagByDefault = true,
93 	.preWriteEntry = beginECtagsFile,
94 	.postWriteEntry = endECTagsFile,
95 	.rescanFailedEntry = NULL,
96 	.treatFieldAsFixed = treatFieldAsFixed,
97 	.defaultFileName = CTAGS_FILE,
98 	.checkOptions = checkCtagsOptions,
99 };
100 
hasTagEntryTabOrNewlineChar(const tagEntryInfo * const tag)101 static bool hasTagEntryTabOrNewlineChar (const tagEntryInfo * const tag)
102 {
103 
104 	if (doesFieldHaveTabOrNewlineChar (FIELD_NAME, tag, NO_PARSER_FIELD)
105 		|| doesFieldHaveTabOrNewlineChar (FIELD_INPUT_FILE, tag, NO_PARSER_FIELD))
106 		return true;
107 
108 	if (tag->lineNumberEntry)
109 	{
110 		if (Option.lineDirectives)
111 		{
112 			if (doesFieldHaveTabOrNewlineChar (FIELD_LINE_NUMBER, tag, NO_PARSER_FIELD))
113 				return true;
114 		}
115 	}
116 	else if (doesFieldHaveTabOrNewlineChar (FIELD_PATTERN, tag, NO_PARSER_FIELD))
117 	{
118 		/* Pattern may have a tab char. However, doesFieldHaveTabOrNewlineChar returns
119 		 * false because NO_PARSER_FIELD may not have doesContainAnyChar handler.
120 		 */
121 		return true;
122 	}
123 
124 	if (includeExtensionFlags ())
125 	{
126 		if (isFieldEnabled (FIELD_SCOPE) && doesFieldHaveValue (FIELD_SCOPE, tag)
127 			&& (doesFieldHaveTabOrNewlineChar (FIELD_SCOPE_KIND_LONG, tag, NO_PARSER_FIELD)
128 				|| doesFieldHaveTabOrNewlineChar (FIELD_SCOPE, tag, NO_PARSER_FIELD)))
129 			return true;
130 		if (isFieldEnabled (FIELD_TYPE_REF) && doesFieldHaveValue (FIELD_TYPE_REF, tag)
131 			&& doesFieldHaveTabOrNewlineChar (FIELD_TYPE_REF, tag, NO_PARSER_FIELD))
132 			return true;
133 		if (isFieldEnabled (FIELD_FILE_SCOPE) && doesFieldHaveValue (FIELD_FILE_SCOPE, tag)
134 			&& doesFieldHaveTabOrNewlineChar (FIELD_FILE_SCOPE, tag, NO_PARSER_FIELD))
135 			return true;
136 
137 		int f[] = { FIELD_INHERITANCE,
138 					FIELD_ACCESS,
139 					FIELD_IMPLEMENTATION,
140 					FIELD_SIGNATURE,
141 					FIELD_ROLES,
142 					FIELD_EXTRAS,
143 					FIELD_XPATH,
144 					FIELD_END_LINE,
145 					-1};
146 		for (unsigned int i = 0; f[i] >= 0; i++)
147 		{
148 			if (isFieldEnabled (f[i]) && doesFieldHaveValue (f[i], tag)
149 				&& doesFieldHaveTabOrNewlineChar (f[i], tag, NO_PARSER_FIELD))
150 				return true;
151 		}
152 	}
153 
154 	for (unsigned int i = 0; i < tag->usedParserFields; i++)
155 	{
156 		const tagField *f = getParserFieldForIndex(tag, i);
157 		fieldType ftype = f->ftype;
158 		if (isFieldEnabled (ftype))
159 		{
160 			if (doesFieldHaveTabOrNewlineChar (ftype, tag, i))
161 				return true;
162 		}
163 	}
164 	return false;
165 }
166 
167 
escapeFieldValueFull(tagWriter * writer,const tagEntryInfo * tag,fieldType ftype,int fieldIndex)168 static const char* escapeFieldValueFull (tagWriter *writer, const tagEntryInfo * tag, fieldType ftype, int fieldIndex)
169 {
170 	const char *v;
171 	if (writer->type == WRITER_E_CTAGS && doesFieldHaveRenderer(ftype, true))
172 		v = renderFieldNoEscaping (ftype, tag, fieldIndex);
173 	else
174 		v = renderField (ftype, tag, fieldIndex);
175 
176 	return v;
177 }
178 
escapeFieldValue(tagWriter * writer,const tagEntryInfo * tag,fieldType ftype)179 static const char* escapeFieldValue (tagWriter *writer, const tagEntryInfo * tag, fieldType ftype)
180 {
181 	return escapeFieldValueFull (writer, tag, ftype, NO_PARSER_FIELD);
182 }
183 
renderExtensionFieldMaybe(tagWriter * writer,int xftype,const tagEntryInfo * const tag,char sep[2],MIO * mio)184 static int renderExtensionFieldMaybe (tagWriter *writer, int xftype, const tagEntryInfo *const tag, char sep[2], MIO *mio)
185 {
186 	if (isFieldEnabled (xftype) && doesFieldHaveValue (xftype, tag))
187 	{
188 		int len;
189 		len = mio_printf (mio, "%s\t%s:%s", sep,
190 				  getFieldName (xftype),
191 				  escapeFieldValue (writer, tag, xftype));
192 		sep[0] = '\0';
193 		return len;
194 	}
195 	else
196 		return 0;
197 }
198 
addParserFields(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag)199 static int addParserFields (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag)
200 {
201 	unsigned int i;
202 	int length = 0;
203 
204 	for (i = 0; i < tag->usedParserFields; i++)
205 	{
206 		const tagField *f = getParserFieldForIndex(tag, i);
207 		fieldType ftype = f->ftype;
208 		if (! isFieldEnabled (ftype))
209 			continue;
210 
211 		length += mio_printf(mio, "\t%s:%s",
212 							 getFieldName (ftype),
213 							 escapeFieldValueFull (writer, tag, ftype, i));
214 	}
215 	return length;
216 }
217 
writeLineNumberEntry(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag)218 static int writeLineNumberEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag)
219 {
220 	if (Option.lineDirectives)
221 		return mio_printf (mio, "%s", escapeFieldValue (writer, tag, FIELD_LINE_NUMBER));
222 	else
223 		return mio_printf (mio, "%lu", tag->lineNumber);
224 }
225 
addExtensionFields(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag)226 static int addExtensionFields (tagWriter *writer, MIO *mio, const tagEntryInfo *const tag)
227 {
228 	bool isKindKeyEnabled = isFieldEnabled (FIELD_KIND_KEY);
229 	bool isScopeEnabled = isFieldEnabled   (FIELD_SCOPE_KEY);
230 
231 	const char* const kindKey = isKindKeyEnabled
232 		?getFieldName (FIELD_KIND_KEY)
233 		:"";
234 	const char* const kindFmt = isKindKeyEnabled
235 		?"%s\t%s:%s"
236 		:"%s\t%s%s";
237 	const char* const scopeKey = isScopeEnabled
238 		?getFieldName (FIELD_SCOPE_KEY)
239 		:"";
240 	const char* const scopeFmt = isScopeEnabled
241 		?"%s\t%s:%s:%s"
242 		:"%s\t%s%s:%s";
243 
244 	char sep [] = {';', '"', '\0'};
245 	int length = 0;
246 
247 	const char *str = NULL;;
248 	kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex);
249 	const char kind_letter_str[2] = {kdef->letter, '\0'};
250 
251 	if (kdef->name != NULL && (isFieldEnabled (FIELD_KIND_LONG)  ||
252 		 (isFieldEnabled (FIELD_KIND)  && kdef->letter == KIND_NULL_LETTER)))
253 	{
254 		/* Use kind long name */
255 		str = kdef->name;
256 	}
257 	else if (kdef->letter != KIND_NULL_LETTER  && (isFieldEnabled (FIELD_KIND) ||
258 			(isFieldEnabled (FIELD_KIND_LONG) &&  kdef->name == NULL)))
259 	{
260 		/* Use kind letter */
261 		str = kind_letter_str;
262 	}
263 
264 	if (str)
265 	{
266 		length += mio_printf (mio, kindFmt, sep, kindKey, str);
267 		sep [0] = '\0';
268 	}
269 
270 	if (isFieldEnabled (FIELD_LINE_NUMBER) &&  doesFieldHaveValue (FIELD_LINE_NUMBER, tag))
271 	{
272 		length += mio_printf (mio, "%s\t%s:%ld", sep,
273 				   getFieldName (FIELD_LINE_NUMBER),
274 				   tag->lineNumber);
275 		sep [0] = '\0';
276 	}
277 
278 	length += renderExtensionFieldMaybe (writer, FIELD_LANGUAGE, tag, sep, mio);
279 
280 	if (isFieldEnabled (FIELD_SCOPE))
281 	{
282 		const char* k, *v;
283 
284 		k = escapeFieldValue (writer, tag, FIELD_SCOPE_KIND_LONG);
285 		v = escapeFieldValue (writer, tag, FIELD_SCOPE);
286 		if (k && v)
287 		{
288 			length += mio_printf (mio, scopeFmt, sep, scopeKey, k, v);
289 			sep [0] = '\0';
290 		}
291 	}
292 
293 	if (isFieldEnabled (FIELD_TYPE_REF) && doesFieldHaveValue (FIELD_TYPE_REF, tag))
294 	{
295 		length += mio_printf (mio, "%s\t%s:%s", sep,
296 				      getFieldName (FIELD_TYPE_REF),
297 				      escapeFieldValue (writer, tag, FIELD_TYPE_REF));
298 		sep [0] = '\0';
299 	}
300 
301 	if (isFieldEnabled (FIELD_FILE_SCOPE) &&  doesFieldHaveValue (FIELD_FILE_SCOPE, tag))
302 	{
303 		length += mio_printf (mio, "%s\t%s:", sep,
304 				      getFieldName (FIELD_FILE_SCOPE));
305 		sep [0] = '\0';
306 	}
307 
308 	for (int k = FIELD_ECTAGS_LOOP_START; k <= FIELD_ECTAGS_LOOP_LAST; k++)
309 		length += renderExtensionFieldMaybe (writer, k, tag, sep, mio);
310 	for (int k = FIELD_UCTAGS_LOOP_START; k <= FIELD_BUILTIN_LAST; k++)
311 		length += renderExtensionFieldMaybe (writer, k, tag, sep, mio);
312 
313 	return length;
314 }
315 
writeCtagsEntry(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag,void * clientData CTAGS_ATTR_UNUSED)316 static int writeCtagsEntry (tagWriter *writer,
317 							MIO * mio, const tagEntryInfo *const tag,
318 							void *clientData CTAGS_ATTR_UNUSED)
319 {
320 	if (writer->private)
321 	{
322 		struct rejection *rej = writer->private;
323 		if (hasTagEntryTabOrNewlineChar (tag))
324 		{
325 			rej->rejectionInThisInput = true;
326 			return 0;
327 		}
328 	}
329 
330 	int length = mio_printf (mio, "%s\t%s\t",
331 			      escapeFieldValue (writer, tag, FIELD_NAME),
332 			      escapeFieldValue (writer, tag, FIELD_INPUT_FILE));
333 
334 	/* This is for handling 'common' of 'fortran'.  See the
335 	   description of --excmd=mixed in ctags.1.  In tags output, what
336 	   we call "pattern" is instructions for vi.
337 
338 	   However, in the other formats, pattern should be pattern as its name. */
339 	if (tag->lineNumberEntry)
340 		length += writeLineNumberEntry (writer, mio, tag);
341 	else
342 	{
343 		if (Option.locate == EX_COMBINE)
344 			length += mio_printf(mio, "%lu;", tag->lineNumber);
345 		length += mio_puts(mio, escapeFieldValue(writer, tag, FIELD_PATTERN));
346 	}
347 
348 	if (includeExtensionFlags ())
349 	{
350 		length += addExtensionFields (writer, mio, tag);
351 		length += addParserFields (writer, mio, tag);
352 	}
353 
354 	length += mio_printf (mio, "\n");
355 
356 	return length;
357 }
358 
writeCtagsPtagEntry(tagWriter * writer CTAGS_ATTR_UNUSED,MIO * mio,const ptagDesc * desc,const char * const fileName,const char * const pattern,const char * const parserName,void * clientData CTAGS_ATTR_UNUSED)359 static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED,
360 				MIO * mio, const ptagDesc *desc,
361 				const char *const fileName,
362 				const char *const pattern,
363 				const char *const parserName,
364 				void *clientData CTAGS_ATTR_UNUSED)
365 {
366 
367 	bool extras = includeExtensionFlags () && isFieldEnabled (FIELD_EXTRAS);
368 	const char *xsep = extras? ";\"	": "";
369 	const char *fsep = extras? ":": "";
370 	const char *fieldx = extras? getFieldName (FIELD_EXTRAS): "";
371 	const char *xptag = extras? getXtagName (XTAG_PSEUDO_TAGS): "";
372 
373 	return parserName
374 
375 #define OPT(X) ((X)?(X):"")
376 		? mio_printf (mio, "%s%s%s%s\t%s\t/%s/%s%s%s%s\n",
377 			      PSEUDO_TAG_PREFIX, desc->name, PSEUDO_TAG_SEPARATOR, parserName,
378 			      OPT(fileName), OPT(pattern),
379 				  xsep, fieldx, fsep, xptag)
380 		: mio_printf (mio, "%s%s\t%s\t/%s/%s%s%s%s\n",
381 			      PSEUDO_TAG_PREFIX, desc->name,
382 			      OPT(fileName), OPT(pattern),
383 			      xsep, fieldx, fsep, xptag);
384 #undef OPT
385 }
386 
387 static fieldType fixedFields [] = {
388 	FIELD_NAME,
389 	FIELD_INPUT_FILE,
390 	FIELD_PATTERN,
391 };
392 
treatFieldAsFixed(int fieldType)393 static bool treatFieldAsFixed (int fieldType)
394 {
395 	for (int i = 0; i < ARRAY_SIZE(fixedFields); i++)
396 		if (fixedFields [i] == fieldType)
397 			return true;
398 	return false;
399 }
400 
checkCtagsOptions(tagWriter * writer CTAGS_ATTR_UNUSED,bool fieldsWereReset)401 static void checkCtagsOptions (tagWriter *writer CTAGS_ATTR_UNUSED,
402 							   bool fieldsWereReset)
403 {
404 	if (isFieldEnabled (FIELD_KIND_KEY)
405 		&& (!(isFieldEnabled (FIELD_KIND_LONG) ||
406 			  isFieldEnabled (FIELD_KIND))))
407 	{
408 		error (WARNING, "though %c/%s field is enabled, neither %c nor %c field is not enabled",
409 			   getFieldLetter (FIELD_KIND_KEY),
410 			   getFieldName (FIELD_KIND_KEY),
411 			   getFieldLetter (FIELD_KIND),
412 			   getFieldLetter (FIELD_KIND_LONG));
413 		error (WARNING, "enable the %c field to make the %c/%s field printable",
414 			   getFieldLetter (FIELD_KIND_LONG),
415 			   getFieldLetter (FIELD_KIND_KEY),
416 			   getFieldName (FIELD_KIND_KEY));
417 		enableField (FIELD_KIND_LONG, true);
418 	}
419 	if (isFieldEnabled (FIELD_SCOPE_KEY)
420 		&& !isFieldEnabled (FIELD_SCOPE))
421 	{
422 		error (WARNING, "though %c/%s field is enabled, %c field is not enabled",
423 			   getFieldLetter (FIELD_SCOPE_KEY),
424 			   getFieldName (FIELD_SCOPE_KEY),
425 			   getFieldLetter (FIELD_SCOPE));
426 		error (WARNING, "enable the %c field to make the %c/%s field printable",
427 			   getFieldLetter (FIELD_SCOPE),
428 			   getFieldLetter (FIELD_SCOPE_KEY),
429 			   getFieldName (FIELD_SCOPE_KEY));
430 		enableField (FIELD_SCOPE, true);
431 	}
432 
433 	for (int i = 0; i < ARRAY_SIZE (fixedFields); i++)
434 	{
435 		if (!isFieldEnabled (fixedFields [i]))
436 		{
437 			enableField (fixedFields [i], true);
438 
439 			if (fieldsWereReset)
440 				continue;
441 
442 			const char *name = getFieldName (fixedFields [i]);
443 			unsigned char letter = getFieldLetter (fixedFields [i]);
444 
445 			if (name && letter != NUL_FIELD_LETTER)
446 				error(WARNING, "Cannot disable fixed field: '%c'{%s} in ctags output mode",
447 				      letter, name);
448 			else if (name)
449 				error(WARNING, "Cannot disable fixed field: {%s} in ctags output mode",
450 				      name);
451 			else if (letter != NUL_FIELD_LETTER)
452 				error(WARNING, "Cannot disable fixed field: '%c' in ctags output mode",
453 				      letter);
454 		}
455 	}
456 }
457