xref: /Universal-ctags/main/mini-geany.c (revision c2ed162e1c737a99a2eb6c23bff05ffdc10343c3)
1f406da36SJiří Techet /*
2f406da36SJiří Techet *   Copyright (c) 2019, Jiri Techet
3f406da36SJiří Techet *
4f406da36SJiří Techet *   This source code is released for free distribution under the terms of the
5f406da36SJiří Techet *   GNU General Public License version 2 or (at your option) any later version.
6f406da36SJiří Techet *
7f406da36SJiří Techet *   Provides a simple application using ctags as a library and using the same
8f406da36SJiří Techet *   set of ctags functions as the Geany editor
9f406da36SJiří Techet */
10f406da36SJiří Techet 
11f406da36SJiří Techet #include "general.h"  /* must always come first */
12f406da36SJiří Techet 
13f406da36SJiří Techet #include "types.h"
14f406da36SJiří Techet #include "routines.h"
15f406da36SJiří Techet #include "mio.h"
16f406da36SJiří Techet #include "error_p.h"
17f406da36SJiří Techet #include "writer_p.h"
18f406da36SJiří Techet #include "parse_p.h"
19f406da36SJiří Techet #include "options_p.h"
20f406da36SJiří Techet #include "trashbox_p.h"
21f406da36SJiří Techet #include "field_p.h"
22f406da36SJiří Techet #include "xtag_p.h"
23f406da36SJiří Techet #include "entry_p.h"
24707278d1SJiří Techet #include "param_p.h"
25f406da36SJiří Techet 
26f406da36SJiří Techet #include <stdio.h>
27f406da36SJiří Techet #include <string.h>
28f406da36SJiří Techet #include <errno.h>
29f406da36SJiří Techet 
30f406da36SJiří Techet static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, void *clientData);
31f406da36SJiří Techet static void rescanFailed (tagWriter *writer, unsigned long validTagNum, void *clientData);
32f406da36SJiří Techet 
33f406da36SJiří Techet /* we need to be able to provide a custom writer using which we collect the tags */
34f406da36SJiří Techet tagWriter customWriter = {
35f406da36SJiří Techet 	.writeEntry = writeEntry,
36f406da36SJiří Techet 	.writePtagEntry = NULL,
37f406da36SJiří Techet 	.preWriteEntry = NULL,
38f406da36SJiří Techet 	.postWriteEntry = NULL,
39f406da36SJiří Techet 	.rescanFailedEntry = rescanFailed,
40f406da36SJiří Techet 	.treatFieldAsFixed = NULL,
41f406da36SJiří Techet 	.defaultFileName = "tags_file_which_should_never_appear_anywhere",
42f406da36SJiří Techet 	.private = NULL,
43f406da36SJiří Techet };
44f406da36SJiří Techet 
45f406da36SJiří Techet 
46f406da36SJiří Techet /* we need to be able to provide an error printer which doesn't crash Geany on error */
nofatalErrorPrinter(const errorSelection selection,const char * const format,va_list ap,void * data CTAGS_ATTR_UNUSED)47f406da36SJiří Techet static bool nofatalErrorPrinter (const errorSelection selection,
48f406da36SJiří Techet 					  const char *const format,
49f406da36SJiří Techet 					  va_list ap, void *data CTAGS_ATTR_UNUSED)
50f406da36SJiří Techet {
51f406da36SJiří Techet 	fprintf (stderr, "%s: ", (selection & WARNING) ? "Warning: " : "Error");
52f406da36SJiří Techet 	vfprintf (stderr, format, ap);
53f406da36SJiří Techet 	if (selection & PERROR)
54f406da36SJiří Techet #ifdef HAVE_STRERROR
55f406da36SJiří Techet 		fprintf (stderr, " : %s", strerror (errno));
56f406da36SJiří Techet #else
57f406da36SJiří Techet 		perror (" ");
58f406da36SJiří Techet #endif
59f406da36SJiří Techet 	fputs ("\n", stderr);
60f406da36SJiří Techet 
61f406da36SJiří Techet 	return false;
62f406da36SJiří Techet }
63f406da36SJiří Techet 
64f406da36SJiří Techet 
enableRoles(unsigned int lang,unsigned int kind)65f473f061SJiří Techet static void enableRoles(unsigned int lang, unsigned int kind)
66f473f061SJiří Techet {
67f473f061SJiří Techet 	unsigned int c = countLanguageRoles(lang, kind);
68f473f061SJiří Techet 
69f473f061SJiří Techet 	for (unsigned int i = 0; i < c; i++)
70f473f061SJiří Techet 	{
71f473f061SJiří Techet 		roleDefinition* rdef = getLanguageRole(lang, kind, (int)i);
72f473f061SJiří Techet 		enableRole(rdef, true);
73f473f061SJiří Techet 	}
74f473f061SJiří Techet }
75f473f061SJiří Techet 
76f473f061SJiří Techet 
77f473f061SJiří Techet /* we need to be able to enable all kinds and roles for all languages (some are disabled by default) */
enableKindsAndRoles()78f473f061SJiří Techet static void enableKindsAndRoles()
79f406da36SJiří Techet {
80f406da36SJiří Techet 	unsigned int lang;
81f406da36SJiří Techet 
82f406da36SJiří Techet 	for (lang = 0; lang < countParsers(); lang++)
83f406da36SJiří Techet 	{
84f406da36SJiří Techet 		unsigned int kindNum = countLanguageKinds(lang);
85f406da36SJiří Techet 		unsigned int kind;
86f406da36SJiří Techet 
87f406da36SJiří Techet 		for (kind = 0; kind < kindNum; kind++)
88f406da36SJiří Techet 		{
89f406da36SJiří Techet 			kindDefinition *def = getLanguageKind(lang, kind);
90f406da36SJiří Techet 			enableKind(def, true);
91f473f061SJiří Techet 			enableRoles(lang, kind);
92f406da36SJiří Techet 		}
93f406da36SJiří Techet 	}
94f406da36SJiří Techet }
95f406da36SJiří Techet 
96f406da36SJiří Techet 
97f406da36SJiří Techet /* we need to be able to initialize ctags like in main() but skipping some things */
ctagsInit(void)98f406da36SJiří Techet static void ctagsInit(void)
99f406da36SJiří Techet {
100f406da36SJiří Techet 	initDefaultTrashBox ();
101f406da36SJiří Techet 
102f406da36SJiří Techet 	setErrorPrinter (nofatalErrorPrinter, NULL);
103f406da36SJiří Techet 	setTagWriter (WRITER_CUSTOM, &customWriter);
104f406da36SJiří Techet 
105f406da36SJiří Techet 	checkRegex ();
106f406da36SJiří Techet 	initFieldObjects ();
107f406da36SJiří Techet 	initXtagObjects ();
108f406da36SJiří Techet 
109f406da36SJiří Techet 	initializeParsing ();
110f406da36SJiří Techet 	initOptions ();
111f13954a7SParallels 	initRegexOptscript ();
112f406da36SJiří Techet 
113f406da36SJiří Techet 	/* make sure all parsers are initialized */
114f406da36SJiří Techet 	initializeParser (LANG_AUTO);
115f406da36SJiří Techet 
116f406da36SJiří Techet 	/* change default value which is false */
117f406da36SJiří Techet 	enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true);
118f406da36SJiří Techet 	enableXtag(XTAG_REFERENCE_TAGS, true);
119f406da36SJiří Techet 
120f473f061SJiří Techet 	/* some kinds and roles we are interested in are disabled by default */
121f473f061SJiří Techet 	enableKindsAndRoles();
122f406da36SJiří Techet }
123f406da36SJiří Techet 
124f406da36SJiří Techet 
125f406da36SJiří Techet /* we need to be able to get a name of a given language */
ctagsGetLangName(int lang)126f406da36SJiří Techet static const char *ctagsGetLangName(int lang)
127f406da36SJiří Techet {
128f406da36SJiří Techet 	return getLanguageName(lang);
129f406da36SJiří Techet }
130f406da36SJiří Techet 
131f406da36SJiří Techet 
132f406da36SJiří Techet /* we need to be able to get an int representing a given language */
ctagsGetNamedLang(const char * name)133f406da36SJiří Techet static int ctagsGetNamedLang(const char *name)
134f406da36SJiří Techet {
135f406da36SJiří Techet 	return getNamedLanguage(name, 0);
136f406da36SJiří Techet }
137f406da36SJiří Techet 
138f406da36SJiří Techet 
139f406da36SJiří Techet /* we need to be able to get kind letters provided by a given language */
ctagsGetLangKinds(int lang)140f406da36SJiří Techet static const char *ctagsGetLangKinds(int lang)
141f406da36SJiří Techet {
142f406da36SJiří Techet 	unsigned int kindNum = countLanguageKinds(lang);
143f406da36SJiří Techet 	static char kinds[257];
144f406da36SJiří Techet 	unsigned int i;
145f406da36SJiří Techet 
146f406da36SJiří Techet 	for (i = 0; i < kindNum; i++)
147f406da36SJiří Techet 		kinds[i] = getLanguageKind(lang, i)->letter;
148f406da36SJiří Techet 	kinds[i] = '\0';
149f406da36SJiří Techet 
150f406da36SJiří Techet 	return kinds;
151f406da36SJiří Techet }
152f406da36SJiří Techet 
153f406da36SJiří Techet 
154f406da36SJiří Techet /* we need to be able to get kind name from a kind letter for a given language */
ctagsGetKindName(char kind,int lang)155f406da36SJiří Techet static const char *ctagsGetKindName(char kind, int lang)
156f406da36SJiří Techet {
157f406da36SJiří Techet 	kindDefinition *def = getLanguageKindForLetter (lang, kind);
158f406da36SJiří Techet 	return def ? def->name : "unknown";
159f406da36SJiří Techet }
160f406da36SJiří Techet 
161f406da36SJiří Techet 
162f406da36SJiří Techet /* we need to be able to get kind letter from a kind name for a given language */
ctagsGetKindFromName(const char * name,int lang)163f406da36SJiří Techet static char ctagsGetKindFromName(const char *name, int lang)
164f406da36SJiří Techet {
165f406da36SJiří Techet 	kindDefinition *def = getLanguageKindForName (lang, name);
166f406da36SJiří Techet 	return def ? def->letter : '-';
167f406da36SJiří Techet }
168f406da36SJiří Techet 
169f406da36SJiří Techet 
170f406da36SJiří Techet /* we need to be able to get kind letter from a kind index */
ctagsGetKindFromIndex(int index,int lang)171f406da36SJiří Techet static char ctagsGetKindFromIndex(int index, int lang)
172f406da36SJiří Techet {
173f406da36SJiří Techet 	return getLanguageKind(lang, index)->letter;
174f406da36SJiří Techet }
175f406da36SJiří Techet 
176f406da36SJiří Techet 
177f406da36SJiří Techet /* we need to be able to get the number of parsers */
ctagsGetLangCount(void)178f406da36SJiří Techet static unsigned int ctagsGetLangCount(void)
179f406da36SJiří Techet {
180f406da36SJiří Techet 	return countParsers();
181f406da36SJiří Techet }
182f406da36SJiří Techet 
183707278d1SJiří Techet 
addIgnoreSymbol(const char * value)184707278d1SJiří Techet void addIgnoreSymbol(const char *value)
185707278d1SJiří Techet {
186707278d1SJiří Techet 	langType lang = getNamedLanguage ("CPreProcessor", 0);
187707278d1SJiří Techet 	applyParameter (lang, "ignore", value);
188707278d1SJiří Techet }
189707278d1SJiří Techet 
190f406da36SJiří Techet /*******************************************************************************
191f406da36SJiří Techet  * So let's just use what we have for our great client...
192f406da36SJiří Techet  ******************************************************************************/
193f406da36SJiří Techet 
194f406da36SJiří Techet /* our internal tag representation - this is all the tag information we use in Geany */
195f406da36SJiří Techet typedef struct {
196f406da36SJiří Techet 	char *name;
197f406da36SJiří Techet 	char *signature;
198f406da36SJiří Techet 	char *scopeName;
199f406da36SJiří Techet 	char *inheritance;
200f406da36SJiří Techet 	char *varType;
201f406da36SJiří Techet 	char *access;
202f406da36SJiří Techet 	char *implementation;
203f406da36SJiří Techet 	char kindLetter;
204f406da36SJiří Techet 	bool isFileScope;
205f406da36SJiří Techet 	unsigned long lineNumber;
206f406da36SJiří Techet 	int lang;
207*c2ed162eSJiří Techet 	bool isAnon;
208f406da36SJiří Techet } Tag;
209f406da36SJiří Techet 
210f406da36SJiří Techet 
createTag(const tagEntryInfo * info)211f406da36SJiří Techet static Tag *createTag(const tagEntryInfo *info)
212f406da36SJiří Techet {
213f406da36SJiří Techet 	Tag *tag = xCalloc(1, Tag);
214f406da36SJiří Techet 	if (info->name)
215f406da36SJiří Techet 		tag->name = eStrdup(info->name);
216f406da36SJiří Techet 	if (info->extensionFields.signature)
217f406da36SJiří Techet 		tag->signature = eStrdup(info->extensionFields.signature);
218f406da36SJiří Techet 	if (info->extensionFields.scopeName)
219f406da36SJiří Techet 		tag->scopeName = eStrdup(info->extensionFields.scopeName);
220f406da36SJiří Techet 	if (info->extensionFields.inheritance)
221f406da36SJiří Techet 		tag->inheritance = eStrdup(info->extensionFields.inheritance);
222f406da36SJiří Techet 	if (info->extensionFields.typeRef[1])
223f406da36SJiří Techet 		tag->varType = eStrdup(info->extensionFields.typeRef[1]);
224f406da36SJiří Techet 	if (info->extensionFields.access)
225f406da36SJiří Techet 		tag->access = eStrdup(info->extensionFields.access);
226f406da36SJiří Techet 	if (info->extensionFields.implementation)
227f406da36SJiří Techet 		tag->implementation = eStrdup(info->extensionFields.implementation);
228f406da36SJiří Techet 	tag->kindLetter = ctagsGetKindFromIndex(info->kindIndex, info->langType);
229f406da36SJiří Techet 	tag->isFileScope = info->isFileScope;
230f406da36SJiří Techet 	tag->lineNumber = info->lineNumber;
231f406da36SJiří Techet 	tag->lang = info->langType;
232*c2ed162eSJiří Techet 	tag->isAnon = isTagExtraBitMarked(info, XTAG_ANONYMOUS);
233f406da36SJiří Techet 	return tag;
234f406da36SJiří Techet }
235f406da36SJiří Techet 
236f406da36SJiří Techet 
destroyTag(Tag * tag)237f406da36SJiří Techet static void destroyTag(Tag *tag)
238f406da36SJiří Techet {
239f406da36SJiří Techet 	if (tag->name)
240f406da36SJiří Techet 		eFree(tag->name);
241f406da36SJiří Techet 	if (tag->signature)
242f406da36SJiří Techet 		eFree(tag->signature);
243f406da36SJiří Techet 	if (tag->scopeName)
244f406da36SJiří Techet 		eFree(tag->scopeName);
245f406da36SJiří Techet 	if (tag->inheritance)
246f406da36SJiří Techet 		eFree(tag->inheritance);
247f406da36SJiří Techet 	if (tag->varType)
248f406da36SJiří Techet 		eFree(tag->varType);
249f406da36SJiří Techet 	if (tag->access)
250f406da36SJiří Techet 		eFree(tag->access);
251f406da36SJiří Techet 	if (tag->implementation)
252f406da36SJiří Techet 		eFree(tag->implementation);
253f406da36SJiří Techet 	eFree(tag);
254f406da36SJiří Techet }
255f406da36SJiří Techet 
256f406da36SJiří Techet 
257f406da36SJiří Techet /* callback from ctags informing us about a new tag */
writeEntry(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag,void * clientData)258f406da36SJiří Techet static int writeEntry (tagWriter *writer, MIO *mio, const tagEntryInfo *const tag, void *clientData)
259f406da36SJiří Techet {
260f406da36SJiří Techet 	Tag *t;
261f406da36SJiří Techet 
262f406da36SJiří Techet 	/* apparently we have to call this to get the scope info - maybe we can
263f406da36SJiří Techet 	 * specify some option during initialization so we don't have to cal this
264f406da36SJiří Techet 	 * ?????? */
265f406da36SJiří Techet 	getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL);
266f406da36SJiří Techet 
267f406da36SJiří Techet 	/* convert tags into our internal representation and store them into an array */
268f406da36SJiří Techet 	t = createTag(tag);
269f406da36SJiří Techet 	ptrArrayAdd((ptrArray *)clientData, t);
270f406da36SJiří Techet 
271f406da36SJiří Techet 	/* output length - we don't write anything to the MIO */
272f406da36SJiří Techet 	return 0;
273f406da36SJiří Techet }
274f406da36SJiří Techet 
275f406da36SJiří Techet 
276f406da36SJiří Techet /* scan has failed so we have invalid tags in our array - validTagNum should
277f406da36SJiří Techet  * tell us the number of valid tags so remove all the extra tags and shrink the array */
rescanFailed(tagWriter * writer,unsigned long validTagNum,void * clientData)278f406da36SJiří Techet static void rescanFailed (tagWriter *writer, unsigned long validTagNum, void *clientData)
279f406da36SJiří Techet {
280f406da36SJiří Techet 	ptrArray *tagArray = clientData;
281f406da36SJiří Techet 	int num = ptrArrayCount(tagArray);
282f406da36SJiří Techet 
283f406da36SJiří Techet 	if (num > validTagNum)
284f406da36SJiří Techet 	{
285f406da36SJiří Techet 		int i;
286f406da36SJiří Techet 		for (i = validTagNum; i < num; i++)
287f406da36SJiří Techet 		{
288f406da36SJiří Techet 			Tag *tag = ptrArrayLast(tagArray);
289f406da36SJiří Techet 			destroyTag(tag);
290f406da36SJiří Techet 			ptrArrayRemoveLast(tagArray);
291f406da36SJiří Techet 		}
292f406da36SJiří Techet 	}
293f406da36SJiří Techet }
294f406da36SJiří Techet 
295f406da36SJiří Techet 
296f406da36SJiří Techet /* do whatever we want to do with the tags */
processCollectedTags(ptrArray * tagArray)297f406da36SJiří Techet static void processCollectedTags(ptrArray *tagArray)
298f406da36SJiří Techet {
299f406da36SJiří Techet 	int i;
300f406da36SJiří Techet 	int num = ptrArrayCount(tagArray);
301f406da36SJiří Techet 
302f406da36SJiří Techet 	for (i = 0; i < num; i++)
303f406da36SJiří Techet 	{
304f406da36SJiří Techet 		Tag *tag = ptrArrayItem(tagArray, i);
305f406da36SJiří Techet 		printf("%s\tline: %lu\tkind: %s\t lang: %s\n",
306f406da36SJiří Techet 			tag->name,
307f406da36SJiří Techet 			tag->lineNumber,
308f406da36SJiří Techet 			ctagsGetKindName(tag->kindLetter, tag->lang),
309f406da36SJiří Techet 			ctagsGetLangName(tag->lang));
310f406da36SJiří Techet 	}
311f406da36SJiří Techet 
312f406da36SJiří Techet 	/* prepare for the next parsing by clearing the tag array */
313f406da36SJiří Techet 	ptrArrayClear(tagArray);
314f406da36SJiří Techet }
315f406da36SJiří Techet 
316f406da36SJiří Techet 
main(int argc,char ** argv)317f406da36SJiří Techet extern int main (int argc, char **argv)
318f406da36SJiří Techet {
319f406da36SJiří Techet 	/* called once when Geany starts */
320f406da36SJiří Techet 	ctagsInit();
321f406da36SJiří Techet 
322f406da36SJiří Techet 	/* create empty tag array *
323f406da36SJiří Techet 	 * this is where we store the collected tags
324f406da36SJiří Techet 	 * NOTE: Geany doesn't use the ptrArray type - it is used just for the purpose of this demo */
325f406da36SJiří Techet 	ptrArray *tagArray = ptrArrayNew((ptrArrayDeleteFunc)destroyTag);
326f406da36SJiří Techet 
327f406da36SJiří Techet 	printf("This parser only parses C files - provide them as arguments on the "
328f406da36SJiří Techet 			"command line or get a hard-coded buffer parsed when no arguments "
329f406da36SJiří Techet 			"are provided\n\n");
330f406da36SJiří Techet 	if (argc == 1)  /* parsing contents of a buffer */
331f406da36SJiří Techet 	{
332707278d1SJiří Techet 		char *program = "FOO int foo() {}\n\n int bar() {}\n\n int main() {}\n";
333f406da36SJiří Techet 		int lang = ctagsGetNamedLang("C");
334f406da36SJiří Techet 		const char *kinds = ctagsGetLangKinds(lang);
335f406da36SJiří Techet 		int i;
336f406da36SJiří Techet 
337707278d1SJiří Techet 		/* we need to be able to set and clear ignore symbols */
338707278d1SJiří Techet 		addIgnoreSymbol("int");
339707278d1SJiří Techet 		/* clear */
340707278d1SJiří Techet 		addIgnoreSymbol(NULL);
341707278d1SJiří Techet 		/* set to something else */
342707278d1SJiří Techet 		addIgnoreSymbol("FOO");
343707278d1SJiří Techet 
344f406da36SJiří Techet 		printf("Number of all parsers is: %d\n", ctagsGetLangCount());
345f406da36SJiří Techet 		printf("We are parsing %s which provides the following kinds:\n",
346f406da36SJiří Techet 			ctagsGetLangName(lang));
347f406da36SJiří Techet 		for (i = 0; kinds[i] != '\0'; i++)
348f406da36SJiří Techet 		{
349f406da36SJiří Techet 			printf("%c: %s\n",
350f406da36SJiří Techet 				/* back and forth conversion - the same like just kinds[i] */
351f406da36SJiří Techet 				ctagsGetKindFromName(ctagsGetKindName(kinds[i], lang), lang),
352f406da36SJiří Techet 				ctagsGetKindName(kinds[i], lang));
353f406da36SJiří Techet 		}
354f406da36SJiří Techet 
355f406da36SJiří Techet 		printf("\nParsing buffer:\n");
356f406da36SJiří Techet 		/* we always specify the language by ourselves and don't use ctags detection */
357f406da36SJiří Techet 		parseRawBuffer("whatever", (unsigned char *)program, strlen(program), lang, tagArray);
358f406da36SJiří Techet 
359f406da36SJiří Techet 		processCollectedTags(tagArray);
360f406da36SJiří Techet 	}
361f406da36SJiří Techet 	else  /* parsing contents of a file */
362f406da36SJiří Techet 	{
363f406da36SJiří Techet 		int i;
364f406da36SJiří Techet 		for (i = 1; i < argc; i++)
365f406da36SJiří Techet 		{
366f406da36SJiří Techet 			printf("\nParsing %s:\n", argv[i]);
367f406da36SJiří Techet 			/* parseRawBuffer() is called repeatadly during Geany execution */
368f406da36SJiří Techet 			parseRawBuffer(argv[i], NULL, 0, getNamedLanguage("C", 0), tagArray);
369f406da36SJiří Techet 
370f406da36SJiří Techet 			processCollectedTags(tagArray);
371f406da36SJiří Techet 		}
372f406da36SJiří Techet 	}
373f406da36SJiří Techet 
374f406da36SJiří Techet 	ptrArrayDelete(tagArray);
375f406da36SJiří Techet 
376f406da36SJiří Techet 	return 0;
377f406da36SJiří Techet }
378