4 * Copyright (c) 1996-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for managing source languages and
10 * dispatching files to the appropriate language parser.
16 #include "general.h" /* must always come first */
36 static parserDefinitionFunc
* BuiltInParsers
[] = { PARSER_LIST
};
37 parserDefinition
** LanguageTable
= NULL
;
38 static unsigned int LanguageCount
= 0;
39 tagEntryFunction TagEntryFunction
= NULL
;
42 * FUNCTION DEFINITIONS
45 extern void makeSimpleTag (
46 const vString
* const name
, kindOption
* const kinds
, const int kind
)
48 if (kinds
[kind
].enabled
&& name
!= NULL
&& vStringLength (name
) > 0)
51 initTagEntry (&e
, vStringValue (name
));
53 e
.kindName
= kinds
[kind
].name
;
54 e
.kind
= kinds
[kind
].letter
;
61 * parserDescription mapping management
64 extern parserDefinition
* parserNew (const char* name
)
66 parserDefinition
* result
= xCalloc (1, parserDefinition
);
67 result
->name
= eStrdup (name
);
71 extern const char *getLanguageName (const langType language
)
74 if (language
== LANG_IGNORE
)
78 Assert (0 <= language
&& language
< (int) LanguageCount
);
79 result
= LanguageTable
[language
]->name
;
84 extern langType
getNamedLanguage (const char *const name
)
86 langType result
= LANG_IGNORE
;
88 Assert (name
!= NULL
);
89 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
91 const parserDefinition
* const lang
= LanguageTable
[i
];
92 if (lang
->name
!= NULL
)
93 if (strcasecmp (name
, lang
->name
) == 0)
99 static langType
getExtensionLanguage (const char *const extension
)
101 langType result
= LANG_IGNORE
;
103 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
105 stringList
* const exts
= LanguageTable
[i
]->currentExtensions
;
106 if (exts
!= NULL
&& stringListExtensionMatched (exts
, extension
))
112 static langType
getPatternLanguage (const char *const fileName
)
114 langType result
= LANG_IGNORE
;
115 const char* base
= baseFilename (fileName
);
117 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
119 stringList
* const ptrns
= LanguageTable
[i
]->currentPatterns
;
120 if (ptrns
!= NULL
&& stringListFileMatched (ptrns
, base
))
126 #ifdef SYS_INTERPRETER
128 /* The name of the language interpreter, either directly or as the argument
131 static vString
* determineInterpreter (const char* const cmd
)
133 vString
* const interpreter
= vStringNew ();
137 vStringClear (interpreter
);
138 for ( ; isspace ((int) *p
) ; ++p
)
140 for ( ; *p
!= '\0' && ! isspace ((int) *p
) ; ++p
)
141 vStringPut (interpreter
, (int) *p
);
142 vStringTerminate (interpreter
);
143 } while (strcmp (vStringValue (interpreter
), "env") == 0);
147 static langType
getInterpreterLanguage (const char *const fileName
)
149 langType result
= LANG_IGNORE
;
150 FILE* const fp
= fopen (fileName
, "r");
153 vString
* const vLine
= vStringNew ();
154 const char* const line
= readLine (vLine
, fp
);
155 if (line
!= NULL
&& line
[0] == '#' && line
[1] == '!')
157 const char* const lastSlash
= strrchr (line
, '/');
158 const char *const cmd
= lastSlash
!= NULL
? lastSlash
+1 : line
+2;
159 vString
* const interpreter
= determineInterpreter (cmd
);
160 result
= getExtensionLanguage (vStringValue (interpreter
));
161 vStringDelete (interpreter
);
163 vStringDelete (vLine
);
171 extern langType
getFileLanguage (const char *const fileName
)
173 langType language
= Option
.language
;
174 if (language
== LANG_AUTO
)
176 language
= getExtensionLanguage (fileExtension (fileName
));
177 if (language
== LANG_IGNORE
)
178 language
= getPatternLanguage (fileName
);
179 #ifdef SYS_INTERPRETER
180 if (language
== LANG_IGNORE
)
182 fileStatus
*status
= eStat (fileName
);
183 if (status
->isExecutable
)
184 language
= getInterpreterLanguage (fileName
);
191 extern void printLanguageMap (const langType language
)
193 boolean first
= TRUE
;
195 stringList
* map
= LanguageTable
[language
]->currentPatterns
;
196 Assert (0 <= language
&& language
< (int) LanguageCount
);
197 for (i
= 0 ; map
!= NULL
&& i
< stringListCount (map
) ; ++i
)
199 printf ("%s(%s)", (first
? "" : " "),
200 vStringValue (stringListItem (map
, i
)));
203 map
= LanguageTable
[language
]->currentExtensions
;
204 for (i
= 0 ; map
!= NULL
&& i
< stringListCount (map
) ; ++i
)
206 printf ("%s.%s", (first
? "" : " "),
207 vStringValue (stringListItem (map
, i
)));
212 extern void installLanguageMapDefault (const langType language
)
214 parserDefinition
* lang
;
215 Assert (0 <= language
&& language
< (int) LanguageCount
);
216 lang
= LanguageTable
[language
];
217 if (lang
->currentPatterns
!= NULL
)
218 stringListDelete (lang
->currentPatterns
);
219 if (lang
->currentExtensions
!= NULL
)
220 stringListDelete (lang
->currentExtensions
);
222 if (lang
->patterns
== NULL
)
223 lang
->currentPatterns
= stringListNew ();
226 lang
->currentPatterns
=
227 stringListNewFromArgv (lang
->patterns
);
229 if (lang
->extensions
== NULL
)
230 lang
->currentExtensions
= stringListNew ();
233 lang
->currentExtensions
=
234 stringListNewFromArgv (lang
->extensions
);
237 printLanguageMap (language
);
241 extern void installLanguageMapDefaults (void)
244 for (i
= 0 ; i
< LanguageCount
; ++i
)
246 verbose (" %s: ", getLanguageName (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 parserDefinition
* lang
;
262 Assert (0 <= language
&& language
< (int) LanguageCount
);
263 lang
= LanguageTable
[language
];
264 if (lang
->currentPatterns
== NULL
)
265 lang
->currentPatterns
= stringListNew ();
266 stringListAdd (lang
->currentPatterns
, str
);
269 extern boolean
removeLanguageExtensionMap (const char *const extension
)
271 boolean result
= FALSE
;
273 for (i
= 0 ; i
< LanguageCount
&& ! result
; ++i
)
275 stringList
* const exts
= LanguageTable
[i
]->currentExtensions
;
276 if (exts
!= NULL
&& stringListRemoveExtension (exts
, extension
))
278 verbose (" (removed from %s)", getLanguageName (i
));
285 extern void addLanguageExtensionMap (
286 const langType language
, const char* extension
)
288 vString
* const str
= vStringNewInit (extension
);
289 Assert (0 <= language
&& language
< (int) LanguageCount
);
290 removeLanguageExtensionMap (extension
);
291 stringListAdd (LanguageTable
[language
]->currentExtensions
, str
);
294 extern void enableLanguage (const langType language
, const boolean state
)
296 Assert (0 <= language
&& language
< (int) LanguageCount
);
297 LanguageTable
[language
]->enabled
= state
;
300 extern void enableLanguages (const boolean state
)
303 for (i
= 0 ; i
< LanguageCount
; ++i
)
304 enableLanguage (i
, state
);
307 static void initializeParsers (void)
310 for (i
= 0 ; i
< LanguageCount
; ++i
)
311 if (LanguageTable
[i
]->initialize
!= NULL
)
312 (LanguageTable
[i
]->initialize
) ((langType
) i
);
315 extern void initializeParsing (void)
317 unsigned int builtInCount
;
320 builtInCount
= sizeof (BuiltInParsers
) / sizeof (BuiltInParsers
[0]);
321 LanguageTable
= xMalloc (builtInCount
, parserDefinition
*);
323 verbose ("Installing parsers: ");
324 for (i
= 0 ; i
< builtInCount
; ++i
)
326 parserDefinition
* const def
= (*BuiltInParsers
[i
]) ();
329 boolean accepted
= FALSE
;
330 if (def
->name
== NULL
|| def
->name
[0] == '\0')
331 error (FATAL
, "parser definition must contain name\n");
335 def
->parser
= findRegexTags
;
339 else if ((def
->parser
== NULL
) == (def
->parser2
== NULL
))
341 "%s parser definition must define one and only one parsing routine\n",
347 verbose ("%s%s", i
> 0 ? ", " : "", def
->name
);
348 def
->id
= LanguageCount
++;
349 LanguageTable
[def
->id
] = def
;
354 enableLanguages (TRUE
);
355 initializeParsers ();
358 extern void freeParserResources (void)
361 for (i
= 0 ; i
< LanguageCount
; ++i
)
363 parserDefinition
* const lang
= LanguageTable
[i
];
364 freeList (&lang
->currentPatterns
);
365 freeList (&lang
->currentExtensions
);
370 if (LanguageTable
!= NULL
)
371 eFree (LanguageTable
);
372 LanguageTable
= NULL
;
380 extern void processLanguageDefineOption (
381 const char *const option
, const char *const parameter __unused__
)
384 if (parameter
[0] == '\0')
385 error (WARNING
, "No language specified for \"%s\" option", option
);
386 else if (getNamedLanguage (parameter
) != LANG_IGNORE
)
387 error (WARNING
, "Language \"%s\" already defined", parameter
);
390 unsigned int i
= LanguageCount
++;
391 parserDefinition
* const def
= parserNew (parameter
);
392 def
->parser
= findRegexTags
;
393 def
->currentPatterns
= stringListNew ();
394 def
->currentExtensions
= stringListNew ();
398 LanguageTable
= xRealloc (LanguageTable
, i
+ 1, parserDefinition
*);
399 LanguageTable
[i
] = def
;
402 error (WARNING
, "regex support not available; required for --%s option",
407 static kindOption
*langKindOption (const langType language
, const int flag
)
410 kindOption
* result
= NULL
;
411 const parserDefinition
* lang
;
412 Assert (0 <= language
&& language
< (int) LanguageCount
);
413 lang
= LanguageTable
[language
];
414 for (i
=0 ; i
< lang
->kindCount
&& result
== NULL
; ++i
)
415 if (lang
->kinds
[i
].letter
== flag
)
416 result
= &lang
->kinds
[i
];
420 static void disableLanguageKinds (const langType language
)
422 const parserDefinition
* lang
;
423 Assert (0 <= language
&& language
< (int) LanguageCount
);
424 lang
= LanguageTable
[language
];
426 disableRegexKinds (language
);
430 for (i
= 0 ; i
< lang
->kindCount
; ++i
)
431 lang
->kinds
[i
].enabled
= FALSE
;
435 static boolean
enableLanguageKind (
436 const langType language
, const int kind
, const boolean mode
)
438 boolean result
= FALSE
;
439 if (LanguageTable
[language
]->regex
)
440 result
= enableRegexKind (language
, kind
, mode
);
443 kindOption
* const opt
= langKindOption (language
, kind
);
453 static void processLangKindOption (
454 const langType language
, const char *const option
,
455 const char *const parameter
)
457 const char *p
= parameter
;
461 Assert (0 <= language
&& language
< (int) LanguageCount
);
462 if (*p
!= '+' && *p
!= '-')
463 disableLanguageKinds (language
);
464 while ((c
= *p
++) != '\0') switch (c
)
466 case '+': mode
= TRUE
; break;
467 case '-': mode
= FALSE
; break;
469 if (! enableLanguageKind (language
, c
, mode
))
470 error (WARNING
, "Unsupported parameter '%c' for --%s option",
476 extern boolean
processKindOption (
477 const char *const option
, const char *const parameter
)
479 boolean handled
= FALSE
;
480 const char* const dash
= strchr (option
, '-');
482 (strcmp (dash
+ 1, "kinds") == 0 || strcmp (dash
+ 1, "types") == 0))
485 vString
* langName
= vStringNew ();
486 vStringNCopyS (langName
, option
, dash
- option
);
487 language
= getNamedLanguage (vStringValue (langName
));
488 if (language
== LANG_IGNORE
)
489 error (WARNING
, "Unknown language specified in \"%s\" option", option
);
491 processLangKindOption (language
, option
, parameter
);
492 vStringDelete (langName
);
498 static void printLanguageKind (const kindOption
* const kind
, boolean indent
)
500 const char *const indentation
= indent
? " " : "";
501 printf ("%s%c %s%s\n", indentation
, kind
->letter
,
502 kind
->description
!= NULL
? kind
->description
:
503 (kind
->name
!= NULL
? kind
->name
: ""),
504 kind
->enabled
? "" : " [off]");
507 static void printKinds (langType language
, boolean indent
)
509 const parserDefinition
* lang
;
510 Assert (0 <= language
&& language
< (int) LanguageCount
);
511 lang
= LanguageTable
[language
];
512 if (lang
->kinds
!= NULL
|| lang
->regex
)
515 for (i
= 0 ; i
< lang
->kindCount
; ++i
)
516 printLanguageKind (lang
->kinds
+ i
, indent
);
517 printRegexKinds (language
, indent
);
521 extern void printLanguageKinds (const langType language
)
523 if (language
== LANG_AUTO
)
526 for (i
= 0 ; i
< LanguageCount
; ++i
)
528 const parserDefinition
* const lang
= LanguageTable
[i
];
529 printf ("%s%s\n", lang
->name
, lang
->enabled
? "" : " [disabled]");
530 printKinds (i
, TRUE
);
534 printKinds (language
, FALSE
);
537 static void printMaps (const langType language
)
539 const parserDefinition
* lang
;
541 Assert (0 <= language
&& language
< (int) LanguageCount
);
542 lang
= LanguageTable
[language
];
543 printf ("%-8s", lang
->name
);
544 if (lang
->currentExtensions
!= NULL
)
545 for (i
= 0 ; i
< stringListCount (lang
->currentExtensions
) ; ++i
)
546 printf (" *.%s", vStringValue (
547 stringListItem (lang
->currentExtensions
, i
)));
548 if (lang
->currentPatterns
!= NULL
)
549 for (i
= 0 ; i
< stringListCount (lang
->currentPatterns
) ; ++i
)
550 printf (" %s", vStringValue (
551 stringListItem (lang
->currentPatterns
, i
)));
555 extern void printLanguageMaps (const langType language
)
557 if (language
== LANG_AUTO
)
560 for (i
= 0 ; i
< LanguageCount
; ++i
)
564 printMaps (language
);
567 static void printLanguage (const langType language
)
569 const parserDefinition
* lang
;
570 Assert (0 <= language
&& language
< (int) LanguageCount
);
571 lang
= LanguageTable
[language
];
572 if (lang
->kinds
!= NULL
|| lang
->regex
)
573 printf ("%s%s\n", lang
->name
, lang
->enabled
? "" : " [disabled]");
576 extern void printLanguageList (void)
579 for (i
= 0 ; i
< LanguageCount
; ++i
)
587 static void makeFileTag (const char *const fileName
)
589 if (Option
.include
.fileNames
)
592 initTagEntry (&tag
, baseFilename (fileName
));
594 tag
.isFileEntry
= TRUE
;
595 tag
.lineNumberEntry
= TRUE
;
597 tag
.kindName
= "file";
604 static boolean
createTagsForFile (
605 const char *const fileName
, const langType language
,
606 const unsigned int passCount
)
608 boolean retried
= FALSE
;
609 Assert (0 <= language
&& language
< (int) LanguageCount
);
610 if (fileOpen (fileName
, language
))
612 const parserDefinition
* const lang
= LanguageTable
[language
];
616 makeFileTag (fileName
);
618 if (lang
->parser
!= NULL
)
620 else if (lang
->parser2
!= NULL
)
621 retried
= lang
->parser2 (passCount
);
624 endEtagsFile (getSourceFileTagPath ());
632 static boolean
createTagsWithFallback (
633 const char *const fileName
, const langType language
)
635 const unsigned long numTags
= TagFile
.numTags
.added
;
636 fpos_t tagFilePosition
;
637 unsigned int passCount
= 0;
638 boolean tagFileResized
= FALSE
;
640 fgetpos (TagFile
.fp
, &tagFilePosition
);
641 while (createTagsForFile (fileName
, language
, ++passCount
))
643 /* Restore prior state of tag file.
645 fsetpos (TagFile
.fp
, &tagFilePosition
);
646 TagFile
.numTags
.added
= numTags
;
647 tagFileResized
= TRUE
;
649 return tagFileResized
;
652 extern boolean
parseFile (const char *const fileName
)
654 boolean tagFileResized
= FALSE
;
655 langType language
= Option
.language
;
656 if (Option
.language
== LANG_AUTO
)
657 language
= getFileLanguage (fileName
);
658 Assert (language
!= LANG_AUTO
);
659 if (language
== LANG_IGNORE
)
660 verbose ("ignoring %s (unknown language)\n", fileName
);
661 else if (! LanguageTable
[language
]->enabled
)
662 verbose ("ignoring %s (language disabled)\n", fileName
);
668 tagFileResized
= createTagsWithFallback (fileName
, language
);
671 closeTagFile (tagFileResized
);
672 addTotals (1, 0L, 0L);
674 return tagFileResized
;
676 return tagFileResized
;
679 /* vi:set tabstop=4 shiftwidth=4 nowrap: */