Update for last 2 commits.
[geany-mirror.git] / tagmanager / parse.c
bloba9c23efd6ce6bdb4574be04b0aba2f60c56c5281
1 /*
3 * Copyright (c) 1996-2001, Darren Hiebert
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License.
8 * This module contains functions for managing source languages and
9 * dispatching files to the appropriate language parser.
13 * INCLUDE FILES
15 #include "general.h" /* must always come first */
17 #include <string.h>
20 #include "entry.h"
21 #include "main.h"
22 #define OPTION_WRITE
23 #include "options.h"
24 #include "parsers.h"
25 #include "read.h"
26 #include "vstring.h"
29 * DATA DEFINITIONS
31 static parserDefinitionFunc* BuiltInParsers[] = { PARSER_LIST };
32 parserDefinition** LanguageTable = NULL;
33 static unsigned int LanguageCount = 0;
34 tagEntryFunction TagEntryFunction = NULL;
35 tagEntrySetArglistFunction TagEntrySetArglistFunction = NULL;
38 * FUNCTION DEFINITIONS
41 extern void makeSimpleTag (const vString* const name,
42 kindOption* const kinds, const int kind)
44 if (name != NULL && vStringLength (name) > 0)
46 tagEntryInfo e;
47 initTagEntry (&e, vStringValue (name));
49 e.kindName = kinds [kind].name;
50 e.kind = kinds [kind].letter;
52 makeTagEntry (&e);
57 extern void makeSimpleScopedTag (const vString* const name,
58 kindOption* const kinds, const int kind,
59 const char* scope, const char *scope2,
60 const char *laccess)
62 if (name != NULL && vStringLength (name) > 0)
64 tagEntryInfo e;
65 initTagEntry (&e, vStringValue (name));
67 e.kindName = kinds [kind].name;
68 e.kind = kinds [kind].letter;
69 e.extensionFields.scope[0] = scope;
70 e.extensionFields.scope[1] = scope2;
71 e.extensionFields.access = laccess;
73 makeTagEntry (&e);
78 * parserDescription mapping management
81 extern parserDefinition* parserNew (const char* name)
83 parserDefinition* result = xCalloc (1, parserDefinition);
84 result->name = eStrdup (name);
85 return result;
88 extern const char *getLanguageName (const langType language)
90 /*Assert (0 <= language && language < (int) LanguageCount);*/
91 if (language < 0) return NULL;
92 return LanguageTable [language]->name;
95 extern langType getNamedLanguage (const char *const name)
97 langType result = LANG_IGNORE;
98 unsigned int i;
99 Assert (name != NULL);
100 for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
102 if (LanguageTable [i]->name != NULL)
103 if (stricmp (name, LanguageTable [i]->name) == 0)
104 result = i;
106 return result;
109 static langType getExtensionLanguage (const char *const extension)
111 langType result = LANG_IGNORE;
112 unsigned int i;
113 for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
115 stringList* const exts = LanguageTable [i]->currentExtensions;
116 if (exts != NULL && stringListExtensionMatched (exts, extension))
117 result = i;
119 return result;
122 static langType getPatternLanguage (const char *const fileName)
124 langType result = LANG_IGNORE;
125 const char* base = baseFilename (fileName);
126 unsigned int i;
127 for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
129 stringList* const ptrns = LanguageTable [i]->currentPatterns;
130 if (ptrns != NULL && stringListFileMatched (ptrns, base))
131 result = i;
133 return result;
136 #ifdef SYS_INTERPRETER
138 /* The name of the language interpreter, either directly or as the argument
139 * to "env".
141 static vString* determineInterpreter (const char* const cmd)
143 vString* const interpreter = vStringNew ();
144 const char* p = cmd;
147 vStringClear (interpreter);
148 for ( ; isspace (*p) ; ++p)
149 ; /* no-op */
150 for ( ; *p != '\0' && ! isspace (*p) ; ++p)
151 vStringPut (interpreter, (int) *p);
152 vStringTerminate (interpreter);
153 } while (strcmp (vStringValue (interpreter), "env") == 0);
154 return interpreter;
157 static langType getInterpreterLanguage (const char *const fileName)
159 langType result = LANG_IGNORE;
160 FILE* const fp = g_fopen (fileName, "r");
161 if (fp != NULL)
163 vString* const vLine = vStringNew ();
164 const char* const line = readLine (vLine, fp);
165 if (line != NULL && line [0] == '#' && line [1] == '!')
167 const char* const lastSlash = strrchr (line, '/');
168 const char *const cmd = lastSlash != NULL ? lastSlash+1 : line+2;
169 vString* const interpreter = determineInterpreter (cmd);
170 result = getExtensionLanguage (vStringValue (interpreter));
171 vStringDelete (interpreter);
173 vStringDelete (vLine);
174 fclose (fp);
176 return result;
179 #endif
181 extern langType getFileLanguage (const char *const fileName)
183 langType language = Option.language;
184 if (language == LANG_AUTO)
186 language = getExtensionLanguage (fileExtension (fileName));
187 if (language == LANG_IGNORE)
188 language = getPatternLanguage (fileName);
189 #ifdef SYS_INTERPRETER
190 if (language == LANG_IGNORE && isExecutable (fileName))
191 language = getInterpreterLanguage (fileName);
192 #endif
194 return language;
197 extern void printLanguageMap (const langType language)
199 boolean first = TRUE;
200 unsigned int i;
201 stringList* map = LanguageTable [language]->currentPatterns;
202 Assert (0 <= language && language < (int) LanguageCount);
203 for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
205 printf ("%s(%s)", (first ? "" : " "),
206 vStringValue (stringListItem (map, i)));
207 first = FALSE;
209 map = LanguageTable [language]->currentExtensions;
210 for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
212 printf ("%s.%s", (first ? "" : " "),
213 vStringValue (stringListItem (map, i)));
214 first = FALSE;
218 extern void installLanguageMapDefault (const langType language)
220 Assert (language >= 0);
221 if (LanguageTable [language]->currentPatterns != NULL)
222 stringListDelete (LanguageTable [language]->currentPatterns);
223 if (LanguageTable [language]->currentExtensions != NULL)
224 stringListDelete (LanguageTable [language]->currentExtensions);
226 if (LanguageTable [language]->patterns == NULL)
227 LanguageTable [language]->currentPatterns = stringListNew ();
228 else
230 LanguageTable [language]->currentPatterns =
231 stringListNewFromArgv (LanguageTable [language]->patterns);
233 if (LanguageTable [language]->extensions == NULL)
234 LanguageTable [language]->currentExtensions = stringListNew ();
235 else
237 LanguageTable [language]->currentExtensions =
238 stringListNewFromArgv (LanguageTable [language]->extensions);
242 extern void installLanguageMapDefaults (void)
244 unsigned int i;
245 for (i = 0 ; i < LanguageCount ; ++i)
247 installLanguageMapDefault (i);
251 extern void clearLanguageMap (const langType language)
253 Assert (0 <= language && language < (int) LanguageCount);
254 stringListClear (LanguageTable [language]->currentPatterns);
255 stringListClear (LanguageTable [language]->currentExtensions);
258 extern void addLanguagePatternMap (const langType language, const char* ptrn)
260 vString* const str = vStringNewInit (ptrn);
261 Assert (0 <= language && language < (int) LanguageCount);
262 if (LanguageTable [language]->currentPatterns == NULL)
263 LanguageTable [language]->currentPatterns = stringListNew ();
264 stringListAdd (LanguageTable [language]->currentPatterns, str);
267 extern void addLanguageExtensionMap (const langType language,
268 const char* extension)
270 vString* const str = vStringNewInit (extension);
271 Assert (0 <= language && language < (int) LanguageCount);
272 stringListAdd (LanguageTable [language]->currentExtensions, str);
275 extern void enableLanguages (const boolean state)
277 unsigned int i;
278 for (i = 0 ; i < LanguageCount ; ++i)
279 LanguageTable [i]->enabled = state;
282 extern void enableLanguage (const langType language, const boolean state)
284 Assert (0 <= language && language < (int) LanguageCount);
285 LanguageTable [language]->enabled = state;
288 static void initializeParsers (void)
290 unsigned int i;
291 for (i = 0 ; i < LanguageCount ; ++i)
292 if (LanguageTable [i]->initialize != NULL)
293 (LanguageTable [i]->initialize) ((langType) i);
296 extern void initializeParsing (void)
298 unsigned int builtInCount;
299 unsigned int i;
301 builtInCount = sizeof (BuiltInParsers) / sizeof (BuiltInParsers [0]);
302 LanguageTable = xMalloc (builtInCount, parserDefinition*);
304 for (i = 0 ; i < builtInCount ; ++i)
306 parserDefinition* const def = (*BuiltInParsers [i]) ();
307 if (def != NULL)
309 boolean accepted = FALSE;
310 if (def->name == NULL || def->name[0] == '\0')
311 error (FATAL, "parser definition must contain name\n");
312 else if (def->regex)
314 #ifdef HAVE_REGEX
315 def->parser = findRegexTags;
316 accepted = TRUE;
317 #endif
319 else if ((def->parser == NULL) == (def->parser2 == NULL))
320 error (FATAL,
321 "%s parser definition must define one and only one parsing routine\n",
322 def->name);
323 else
324 accepted = TRUE;
325 if (accepted)
327 def->id = LanguageCount++;
328 LanguageTable [def->id] = def;
332 enableLanguages (TRUE);
333 initializeParsers ();
336 extern void freeParserResources (void)
338 unsigned int i;
339 for (i = 0 ; i < LanguageCount ; ++i)
341 freeList (&LanguageTable [i]->currentPatterns);
342 freeList (&LanguageTable [i]->currentExtensions);
343 eFree (LanguageTable [i]->name);
344 LanguageTable [i]->name = NULL;
345 eFree (LanguageTable [i]);
347 eFree (LanguageTable);
348 LanguageTable = NULL;
349 LanguageCount = 0;
353 * Option parsing
356 extern void processLanguageDefineOption (const char *const option,
357 const char *const __unused__ parameter)
359 #ifdef HAVE_REGEX
360 if (parameter [0] == '\0')
361 error (WARNING, "No language specified for \"%s\" option", option);
362 else if (getNamedLanguage (parameter) != LANG_IGNORE)
363 error (WARNING, "Language \"%s\" already defined", parameter);
364 else
366 unsigned int i = LanguageCount++;
367 parserDefinition* const def = parserNew (parameter);
368 def->parser = findRegexTags;
369 def->currentPatterns = stringListNew ();
370 def->currentExtensions = stringListNew ();
371 def->regex = TRUE;
372 def->enabled = TRUE;
373 def->id = i;
374 LanguageTable = xRealloc (LanguageTable, i + 1, parserDefinition*);
375 LanguageTable [i] = def;
377 #else
378 error (WARNING, "regex support not available; required for --%s option",
379 option);
380 #endif
383 static kindOption *langKindOption (const langType language, const int flag)
385 unsigned int i;
386 kindOption* result = NULL;
387 const parserDefinition* lang;
388 Assert (0 <= language && language < (int) LanguageCount);
389 lang = LanguageTable [language];
390 for (i=0 ; i < lang->kindCount && result == NULL ; ++i)
391 if (lang->kinds [i].letter == flag)
392 result = &lang->kinds [i];
393 return result;
396 extern void processLegacyKindOption (const char *const parameter)
398 const langType lang = getNamedLanguage ("c");
399 boolean clear = FALSE;
400 const char* p = parameter;
401 boolean mode = TRUE;
402 int c;
404 error (WARNING, "-i option is deprecated; use --c-types option instead");
405 if (*p == '=')
407 clear = TRUE;
408 ++p;
410 if (clear && *p != '+' && *p != '-')
412 unsigned int i;
413 for (i = 0 ; i < LanguageTable [lang]->kindCount ; ++i)
414 LanguageTable [lang]->kinds [i].enabled = FALSE;
415 Option.include.fileNames= FALSE;
416 Option.include.fileScope= FALSE;
418 while ((c = *p++) != '\0') switch (c)
420 case '+': mode = TRUE; break;
421 case '-': mode = FALSE; break;
423 case 'F': Option.include.fileNames = mode; break;
424 case 'S': Option.include.fileScope = mode; break;
426 default:
428 kindOption* const opt = langKindOption (lang, c);
429 if (opt != NULL)
430 opt->enabled = mode;
431 else
432 error (WARNING, "Unsupported parameter '%c' for -i option", c);
433 } break;
437 static void disableLanguageKinds (const langType language)
439 if (LanguageTable [language]->regex)
440 #ifdef HAVE_REGEX
441 disableRegexKinds (language);
442 #else
444 #endif
445 else
447 unsigned int i;
448 for (i = 0 ; i < LanguageTable [language]->kindCount ; ++i)
449 LanguageTable [language]->kinds [i].enabled = FALSE;
453 static boolean enableLanguageKind (const langType language,
454 const int kind, const boolean mode)
456 boolean result = FALSE;
457 if (LanguageTable [language]->regex)
458 #ifdef HAVE_REGEX
459 result = enableRegexKind (language, kind, mode);
460 #else
462 #endif
463 else
465 kindOption* const opt = langKindOption (language, kind);
466 if (opt != NULL)
468 opt->enabled = mode;
469 result = TRUE;
472 return result;
475 static void processLangKindOption (const langType language,
476 const char *const option,
477 const char *const parameter)
479 const char *p = parameter;
480 boolean mode = TRUE;
481 int c;
483 Assert (0 <= language && language < (int) LanguageCount);
484 if (*p != '+' && *p != '-')
485 disableLanguageKinds (language);
486 while ((c = *p++) != '\0') switch (c)
488 case '+': mode = TRUE; break;
489 case '-': mode = FALSE; break;
491 default:
493 if (! enableLanguageKind (language, c, mode))
494 error (WARNING, "Unsupported parameter '%c' for --%s option",
495 c, option);
496 } break;
500 extern boolean processKindOption (const char *const option,
501 const char *const parameter)
503 boolean handled = FALSE;
504 const char* const dash = strchr (option, '-');
505 if (dash != NULL &&
506 (strcmp (dash + 1, "types") == 0 || strcmp (dash + 1, "kinds") == 0))
508 langType language;
509 vString* langName = vStringNew ();
510 vStringNCopyS (langName, option, dash - option);
511 language = getNamedLanguage (vStringValue (langName));
512 if (language == LANG_IGNORE)
513 error (WARNING, "Unknown language specified in \"%s\" option", option);
514 else
515 processLangKindOption (language, option, parameter);
516 vStringDelete (langName);
517 handled = TRUE;
519 return handled;
522 static void printLangugageKindOption (const kindOption* const kind)
524 printf (" %c %s%s\n", kind->letter,
525 kind->description != NULL ? kind->description :
526 (kind->name != NULL ? kind->name : ""),
527 kind->enabled ? "" : " [off]");
530 static void printLangugageKindOptions (const langType language)
532 const parserDefinition* lang;
533 Assert (0 <= language && language < (int) LanguageCount);
534 lang = LanguageTable [language];
535 if (lang->kinds != NULL || lang->regex)
537 unsigned int i;
538 char* const name = newLowerString (lang->name);
539 printf (" --%s-types=[+|-]kinds\n", name);
540 eFree (name);
541 if (lang->kinds != NULL)
542 for (i = 0 ; i < lang->kindCount ; ++i)
543 printLangugageKindOption (lang->kinds + i);
544 #ifdef HAVE_REGEX
545 /*printRegexKindOptions (language);*/ /* unused */
546 #endif
550 extern void printKindOptions (void)
552 unsigned int i;
554 printf (
555 "\n The following options are used to specify which language-specific tag\n\
556 types (or kinds) should be included in the tag file. \"Kinds\" is a group of\n\
557 one-letter flags designating kinds of tags to either include or exclude from\n\
558 the output. Each letter or group of letters may be preceded by either '+' to\n\
559 add it to those already included, or '-' to exclude it from the output. In\n\
560 the absence of any preceding '+' or '-' sign, only those kinds listed in\n\
561 \"kinds\" will be included in the output. Below each option is a list of the\n\
562 flags accepted. All kinds are enabled by default unless otherwise noted.\n\n");
564 for (i = 0 ; i < LanguageCount ; ++i)
565 printLangugageKindOptions (i);
569 * File parsing
572 static void makeFileTag (const char *const fileName)
574 if (Option.include.fileNames)
576 tagEntryInfo tag;
577 initTagEntry (&tag, baseFilename (fileName));
579 tag.isFileEntry = TRUE;
580 tag.lineNumberEntry = TRUE;
581 tag.lineNumber = 1;
582 tag.kindName = "file";
583 tag.kind = 'F';
585 makeTagEntry (&tag);
589 static boolean createTagsForFile (const char *const fileName,
590 const langType language,
591 const unsigned int passCount)
593 boolean retried = FALSE;
595 if (fileOpen (fileName, language))
598 makeFileTag (fileName);
600 if (LanguageTable [language]->parser != NULL)
601 LanguageTable [language]->parser ();
602 else if (LanguageTable [language]->parser2 != NULL)
603 retried = LanguageTable [language]->parser2 (passCount);
606 fileClose ();
609 return retried;
612 static boolean createTagsWithFallback (const char *const fileName,
613 const langType language)
615 const unsigned long numTags = TagFile.numTags.added;
616 fpos_t tagFilePosition;
617 unsigned int passCount = 0;
618 boolean tagFileResized = FALSE;
620 fgetpos (TagFile.fp, &tagFilePosition);
621 while (createTagsForFile (fileName, language, ++passCount))
623 /* Restore prior state of tag file.
625 fsetpos (TagFile.fp, &tagFilePosition);
626 TagFile.numTags.added = numTags;
627 tagFileResized = TRUE;
629 return tagFileResized;
632 extern boolean parseFile (const char *const fileName)
634 boolean tagFileResized = FALSE;
635 langType language = Option.language;
636 if (Option.language == LANG_AUTO)
637 language = getFileLanguage (fileName);
638 Assert (language != LANG_AUTO);
639 if (Option.filter)
640 openTagFile ();
642 tagFileResized = createTagsWithFallback (fileName, language);
644 addTotals (1, 0L, 0L);
646 return tagFileResized;
649 /* vi:set tabstop=8 shiftwidth=4 nowrap: */