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