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.
15 #include "general.h" /* must always come first */
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)
47 initTagEntry (&e
, vStringValue (name
));
49 e
.kindName
= kinds
[kind
].name
;
50 e
.kind
= kinds
[kind
].letter
;
57 extern void makeSimpleScopedTag (const vString
* const name
,
58 kindOption
* const kinds
, const int kind
,
59 const char* scope
, const char *scope2
,
62 if (name
!= NULL
&& vStringLength (name
) > 0)
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
;
78 * parserDescription mapping management
81 extern parserDefinition
* parserNew (const char* name
)
83 parserDefinition
* result
= xCalloc (1, parserDefinition
);
84 result
->name
= eStrdup (name
);
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
;
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)
109 static langType
getExtensionLanguage (const char *const extension
)
111 langType result
= LANG_IGNORE
;
113 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
115 stringList
* const exts
= LanguageTable
[i
]->currentExtensions
;
116 if (exts
!= NULL
&& stringListExtensionMatched (exts
, extension
))
122 static langType
getPatternLanguage (const char *const fileName
)
124 langType result
= LANG_IGNORE
;
125 const char* base
= baseFilename (fileName
);
127 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
129 stringList
* const ptrns
= LanguageTable
[i
]->currentPatterns
;
130 if (ptrns
!= NULL
&& stringListFileMatched (ptrns
, base
))
136 #ifdef SYS_INTERPRETER
138 /* The name of the language interpreter, either directly or as the argument
141 static vString
* determineInterpreter (const char* const cmd
)
143 vString
* const interpreter
= vStringNew ();
147 vStringClear (interpreter
);
148 for ( ; isspace (*p
) ; ++p
)
150 for ( ; *p
!= '\0' && ! isspace (*p
) ; ++p
)
151 vStringPut (interpreter
, (int) *p
);
152 vStringTerminate (interpreter
);
153 } while (strcmp (vStringValue (interpreter
), "env") == 0);
157 static langType
getInterpreterLanguage (const char *const fileName
)
159 langType result
= LANG_IGNORE
;
160 FILE* const fp
= g_fopen (fileName
, "r");
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
);
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
);
197 extern void printLanguageMap (const langType language
)
199 boolean first
= TRUE
;
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
)));
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
)));
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 ();
230 LanguageTable
[language
]->currentPatterns
=
231 stringListNewFromArgv (LanguageTable
[language
]->patterns
);
233 if (LanguageTable
[language
]->extensions
== NULL
)
234 LanguageTable
[language
]->currentExtensions
= stringListNew ();
237 LanguageTable
[language
]->currentExtensions
=
238 stringListNewFromArgv (LanguageTable
[language
]->extensions
);
242 extern void installLanguageMapDefaults (void)
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
)
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)
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
;
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
]) ();
309 boolean accepted
= FALSE
;
310 if (def
->name
== NULL
|| def
->name
[0] == '\0')
311 error (FATAL
, "parser definition must contain name\n");
315 def
->parser
= findRegexTags
;
319 else if ((def
->parser
== NULL
) == (def
->parser2
== NULL
))
321 "%s parser definition must define one and only one parsing routine\n",
327 def
->id
= LanguageCount
++;
328 LanguageTable
[def
->id
] = def
;
332 enableLanguages (TRUE
);
333 initializeParsers ();
336 extern void freeParserResources (void)
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
;
356 extern void processLanguageDefineOption (const char *const option
,
357 const char *const __unused__ parameter
)
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
);
366 unsigned int i
= LanguageCount
++;
367 parserDefinition
* const def
= parserNew (parameter
);
368 def
->parser
= findRegexTags
;
369 def
->currentPatterns
= stringListNew ();
370 def
->currentExtensions
= stringListNew ();
374 LanguageTable
= xRealloc (LanguageTable
, i
+ 1, parserDefinition
*);
375 LanguageTable
[i
] = def
;
378 error (WARNING
, "regex support not available; required for --%s option",
383 static kindOption
*langKindOption (const langType language
, const int flag
)
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
];
396 extern void processLegacyKindOption (const char *const parameter
)
398 const langType lang
= getNamedLanguage ("c");
399 boolean clear
= FALSE
;
400 const char* p
= parameter
;
404 error (WARNING
, "-i option is deprecated; use --c-types option instead");
410 if (clear
&& *p
!= '+' && *p
!= '-')
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;
428 kindOption
* const opt
= langKindOption (lang
, c
);
432 error (WARNING
, "Unsupported parameter '%c' for -i option", c
);
437 static void disableLanguageKinds (const langType language
)
439 if (LanguageTable
[language
]->regex
)
441 disableRegexKinds (language
);
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
)
459 result
= enableRegexKind (language
, kind
, mode
);
465 kindOption
* const opt
= langKindOption (language
, kind
);
475 static void processLangKindOption (const langType language
,
476 const char *const option
,
477 const char *const parameter
)
479 const char *p
= parameter
;
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;
493 if (! enableLanguageKind (language
, c
, mode
))
494 error (WARNING
, "Unsupported parameter '%c' for --%s option",
500 extern boolean
processKindOption (const char *const option
,
501 const char *const parameter
)
503 boolean handled
= FALSE
;
504 const char* const dash
= strchr (option
, '-');
506 (strcmp (dash
+ 1, "types") == 0 || strcmp (dash
+ 1, "kinds") == 0))
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
);
515 processLangKindOption (language
, option
, parameter
);
516 vStringDelete (langName
);
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
)
538 char* const name
= newLowerString (lang
->name
);
539 printf (" --%s-types=[+|-]kinds\n", name
);
541 if (lang
->kinds
!= NULL
)
542 for (i
= 0 ; i
< lang
->kindCount
; ++i
)
543 printLangugageKindOption (lang
->kinds
+ i
);
545 /*printRegexKindOptions (language);*/ /* unused */
550 extern void printKindOptions (void)
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
);
572 static void makeFileTag (const char *const fileName
)
574 if (Option
.include
.fileNames
)
577 initTagEntry (&tag
, baseFilename (fileName
));
579 tag
.isFileEntry
= TRUE
;
580 tag
.lineNumberEntry
= TRUE
;
582 tag
.kindName
= "file";
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
);
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
);
642 tagFileResized
= createTagsWithFallback (fileName
, language
);
644 addTotals (1, 0L, 0L);
646 return tagFileResized
;
649 /* vi:set tabstop=8 shiftwidth=4 nowrap: */