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 */
32 static parserDefinitionFunc
* BuiltInParsers
[] = { PARSER_LIST
};
33 parserDefinition
** LanguageTable
= NULL
;
34 static unsigned int LanguageCount
= 0;
35 tagEntryFunction TagEntryFunction
= NULL
;
36 tagEntrySetArglistFunction TagEntrySetArglistFunction
= NULL
;
39 * FUNCTION DEFINITIONS
42 extern void makeSimpleTag (const vString
* const name
,
43 kindOption
* const kinds
, const int kind
)
45 if (name
!= NULL
&& vStringLength (name
) > 0)
48 initTagEntry (&e
, vStringValue (name
));
50 e
.kindName
= kinds
[kind
].name
;
51 e
.kind
= kinds
[kind
].letter
;
58 extern void makeSimpleScopedTag (const vString
* const name
,
59 kindOption
* const kinds
, const int kind
,
60 const char* scope
, const char *scope2
,
63 if (name
!= NULL
&& vStringLength (name
) > 0)
66 initTagEntry (&e
, vStringValue (name
));
68 e
.kindName
= kinds
[kind
].name
;
69 e
.kind
= kinds
[kind
].letter
;
70 e
.extensionFields
.scope
[0] = scope
;
71 e
.extensionFields
.scope
[1] = scope2
;
72 e
.extensionFields
.access
= laccess
;
79 * parserDescription mapping management
82 extern parserDefinition
* parserNew (const char* name
)
84 parserDefinition
* result
= xCalloc (1, parserDefinition
);
85 result
->name
= eStrdup (name
);
89 extern const char *getLanguageName (const langType language
)
91 /*Assert (0 <= language && language < (int) LanguageCount);*/
92 if (language
< 0) return NULL
;
93 return LanguageTable
[language
]->name
;
96 extern langType
getNamedLanguage (const char *const name
)
98 langType result
= LANG_IGNORE
;
100 Assert (name
!= NULL
);
101 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
103 if (LanguageTable
[i
]->name
!= NULL
)
104 if (stricmp (name
, LanguageTable
[i
]->name
) == 0)
110 static langType
getExtensionLanguage (const char *const extension
)
112 langType result
= LANG_IGNORE
;
114 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
116 stringList
* const exts
= LanguageTable
[i
]->currentExtensions
;
117 if (exts
!= NULL
&& stringListExtensionMatched (exts
, extension
))
123 static langType
getPatternLanguage (const char *const fileName
)
125 langType result
= LANG_IGNORE
;
126 const char* base
= baseFilename (fileName
);
128 for (i
= 0 ; i
< LanguageCount
&& result
== LANG_IGNORE
; ++i
)
130 stringList
* const ptrns
= LanguageTable
[i
]->currentPatterns
;
131 if (ptrns
!= NULL
&& stringListFileMatched (ptrns
, base
))
137 #ifdef SYS_INTERPRETER
139 /* The name of the language interpreter, either directly or as the argument
142 static vString
* determineInterpreter (const char* const cmd
)
144 vString
* const interpreter
= vStringNew ();
148 vStringClear (interpreter
);
149 for ( ; isspace (*p
) ; ++p
)
151 for ( ; *p
!= '\0' && ! isspace (*p
) ; ++p
)
152 vStringPut (interpreter
, (int) *p
);
153 vStringTerminate (interpreter
);
154 } while (strcmp (vStringValue (interpreter
), "env") == 0);
158 static langType
getInterpreterLanguage (const char *const fileName
)
160 langType result
= LANG_IGNORE
;
161 FILE* const fp
= g_fopen (fileName
, "r");
164 vString
* const vLine
= vStringNew ();
165 const char* const line
= readLine (vLine
, fp
);
166 if (line
!= NULL
&& line
[0] == '#' && line
[1] == '!')
168 const char* const lastSlash
= strrchr (line
, '/');
169 const char *const cmd
= lastSlash
!= NULL
? lastSlash
+1 : line
+2;
170 vString
* const interpreter
= determineInterpreter (cmd
);
171 result
= getExtensionLanguage (vStringValue (interpreter
));
172 vStringDelete (interpreter
);
174 vStringDelete (vLine
);
182 extern langType
getFileLanguage (const char *const fileName
)
184 langType language
= Option
.language
;
185 if (language
== LANG_AUTO
)
187 language
= getExtensionLanguage (fileExtension (fileName
));
188 if (language
== LANG_IGNORE
)
189 language
= getPatternLanguage (fileName
);
190 #ifdef SYS_INTERPRETER
191 if (language
== LANG_IGNORE
&& isExecutable (fileName
))
192 language
= getInterpreterLanguage (fileName
);
198 extern void printLanguageMap (const langType language
)
200 boolean first
= TRUE
;
202 stringList
* map
= LanguageTable
[language
]->currentPatterns
;
203 Assert (0 <= language
&& language
< (int) LanguageCount
);
204 for (i
= 0 ; map
!= NULL
&& i
< stringListCount (map
) ; ++i
)
206 printf ("%s(%s)", (first
? "" : " "),
207 vStringValue (stringListItem (map
, i
)));
210 map
= LanguageTable
[language
]->currentExtensions
;
211 for (i
= 0 ; map
!= NULL
&& i
< stringListCount (map
) ; ++i
)
213 printf ("%s.%s", (first
? "" : " "),
214 vStringValue (stringListItem (map
, i
)));
219 extern void installLanguageMapDefault (const langType language
)
221 Assert (language
>= 0);
222 if (LanguageTable
[language
]->currentPatterns
!= NULL
)
223 stringListDelete (LanguageTable
[language
]->currentPatterns
);
224 if (LanguageTable
[language
]->currentExtensions
!= NULL
)
225 stringListDelete (LanguageTable
[language
]->currentExtensions
);
227 if (LanguageTable
[language
]->patterns
== NULL
)
228 LanguageTable
[language
]->currentPatterns
= stringListNew ();
231 LanguageTable
[language
]->currentPatterns
=
232 stringListNewFromArgv (LanguageTable
[language
]->patterns
);
234 if (LanguageTable
[language
]->extensions
== NULL
)
235 LanguageTable
[language
]->currentExtensions
= stringListNew ();
238 LanguageTable
[language
]->currentExtensions
=
239 stringListNewFromArgv (LanguageTable
[language
]->extensions
);
243 extern void installLanguageMapDefaults (void)
246 for (i
= 0 ; i
< LanguageCount
; ++i
)
248 installLanguageMapDefault (i
);
252 extern void clearLanguageMap (const langType language
)
254 Assert (0 <= language
&& language
< (int) LanguageCount
);
255 stringListClear (LanguageTable
[language
]->currentPatterns
);
256 stringListClear (LanguageTable
[language
]->currentExtensions
);
259 extern void addLanguagePatternMap (const langType language
, const char* ptrn
)
261 vString
* const str
= vStringNewInit (ptrn
);
262 Assert (0 <= language
&& language
< (int) LanguageCount
);
263 if (LanguageTable
[language
]->currentPatterns
== NULL
)
264 LanguageTable
[language
]->currentPatterns
= stringListNew ();
265 stringListAdd (LanguageTable
[language
]->currentPatterns
, str
);
268 extern void addLanguageExtensionMap (const langType language
,
269 const char* extension
)
271 vString
* const str
= vStringNewInit (extension
);
272 Assert (0 <= language
&& language
< (int) LanguageCount
);
273 stringListAdd (LanguageTable
[language
]->currentExtensions
, str
);
276 extern void enableLanguages (const boolean state
)
279 for (i
= 0 ; i
< LanguageCount
; ++i
)
280 LanguageTable
[i
]->enabled
= state
;
283 extern void enableLanguage (const langType language
, const boolean state
)
285 Assert (0 <= language
&& language
< (int) LanguageCount
);
286 LanguageTable
[language
]->enabled
= state
;
289 static void initializeParsers (void)
292 for (i
= 0 ; i
< LanguageCount
; ++i
)
293 if (LanguageTable
[i
]->initialize
!= NULL
)
294 (LanguageTable
[i
]->initialize
) ((langType
) i
);
297 extern void initializeParsing (void)
299 unsigned int builtInCount
;
302 builtInCount
= sizeof (BuiltInParsers
) / sizeof (BuiltInParsers
[0]);
303 LanguageTable
= xMalloc (builtInCount
, parserDefinition
*);
305 for (i
= 0 ; i
< builtInCount
; ++i
)
307 parserDefinition
* const def
= (*BuiltInParsers
[i
]) ();
310 boolean accepted
= FALSE
;
311 if (def
->name
== NULL
|| def
->name
[0] == '\0')
312 error (FATAL
, "parser definition must contain name\n");
316 def
->parser
= findRegexTags
;
320 else if ((def
->parser
== NULL
) == (def
->parser2
== NULL
))
322 "%s parser definition must define one and only one parsing routine\n",
328 def
->id
= LanguageCount
++;
329 LanguageTable
[def
->id
] = def
;
333 enableLanguages (TRUE
);
334 initializeParsers ();
337 extern void freeParserResources (void)
340 for (i
= 0 ; i
< LanguageCount
; ++i
)
342 freeList (&LanguageTable
[i
]->currentPatterns
);
343 freeList (&LanguageTable
[i
]->currentExtensions
);
344 eFree (LanguageTable
[i
]->name
);
345 LanguageTable
[i
]->name
= NULL
;
346 eFree (LanguageTable
[i
]);
348 eFree (LanguageTable
);
349 LanguageTable
= NULL
;
357 extern void processLanguageDefineOption (const char *const option
,
358 const char *const __unused__ parameter
)
361 if (parameter
[0] == '\0')
362 error (WARNING
, "No language specified for \"%s\" option", option
);
363 else if (getNamedLanguage (parameter
) != LANG_IGNORE
)
364 error (WARNING
, "Language \"%s\" already defined", parameter
);
367 unsigned int i
= LanguageCount
++;
368 parserDefinition
* const def
= parserNew (parameter
);
369 def
->parser
= findRegexTags
;
370 def
->currentPatterns
= stringListNew ();
371 def
->currentExtensions
= stringListNew ();
375 LanguageTable
= xRealloc (LanguageTable
, i
+ 1, parserDefinition
*);
376 LanguageTable
[i
] = def
;
379 error (WARNING
, "regex support not available; required for --%s option",
384 static kindOption
*langKindOption (const langType language
, const int flag
)
387 kindOption
* result
= NULL
;
388 const parserDefinition
* lang
;
389 Assert (0 <= language
&& language
< (int) LanguageCount
);
390 lang
= LanguageTable
[language
];
391 for (i
=0 ; i
< lang
->kindCount
&& result
== NULL
; ++i
)
392 if (lang
->kinds
[i
].letter
== flag
)
393 result
= &lang
->kinds
[i
];
397 extern void processLegacyKindOption (const char *const parameter
)
399 const langType lang
= getNamedLanguage ("c");
400 boolean clear
= FALSE
;
401 const char* p
= parameter
;
405 error (WARNING
, "-i option is deprecated; use --c-types option instead");
411 if (clear
&& *p
!= '+' && *p
!= '-')
414 for (i
= 0 ; i
< LanguageTable
[lang
]->kindCount
; ++i
)
415 LanguageTable
[lang
]->kinds
[i
].enabled
= FALSE
;
416 Option
.include
.fileNames
= FALSE
;
417 Option
.include
.fileScope
= FALSE
;
419 while ((c
= *p
++) != '\0') switch (c
)
421 case '+': mode
= TRUE
; break;
422 case '-': mode
= FALSE
; break;
424 case 'F': Option
.include
.fileNames
= mode
; break;
425 case 'S': Option
.include
.fileScope
= mode
; break;
429 kindOption
* const opt
= langKindOption (lang
, c
);
433 error (WARNING
, "Unsupported parameter '%c' for -i option", c
);
438 static void disableLanguageKinds (const langType language
)
440 if (LanguageTable
[language
]->regex
)
442 disableRegexKinds (language
);
449 for (i
= 0 ; i
< LanguageTable
[language
]->kindCount
; ++i
)
450 LanguageTable
[language
]->kinds
[i
].enabled
= FALSE
;
454 static boolean
enableLanguageKind (const langType language
,
455 const int kind
, const boolean mode
)
457 boolean result
= FALSE
;
458 if (LanguageTable
[language
]->regex
)
460 result
= enableRegexKind (language
, kind
, mode
);
466 kindOption
* const opt
= langKindOption (language
, kind
);
476 static void processLangKindOption (const langType language
,
477 const char *const option
,
478 const char *const parameter
)
480 const char *p
= parameter
;
484 Assert (0 <= language
&& language
< (int) LanguageCount
);
485 if (*p
!= '+' && *p
!= '-')
486 disableLanguageKinds (language
);
487 while ((c
= *p
++) != '\0') switch (c
)
489 case '+': mode
= TRUE
; break;
490 case '-': mode
= FALSE
; break;
494 if (! enableLanguageKind (language
, c
, mode
))
495 error (WARNING
, "Unsupported parameter '%c' for --%s option",
501 extern boolean
processKindOption (const char *const option
,
502 const char *const parameter
)
504 boolean handled
= FALSE
;
505 const char* const dash
= strchr (option
, '-');
507 (strcmp (dash
+ 1, "types") == 0 || strcmp (dash
+ 1, "kinds") == 0))
510 vString
* langName
= vStringNew ();
511 vStringNCopyS (langName
, option
, dash
- option
);
512 language
= getNamedLanguage (vStringValue (langName
));
513 if (language
== LANG_IGNORE
)
514 error (WARNING
, "Unknown language specified in \"%s\" option", option
);
516 processLangKindOption (language
, option
, parameter
);
517 vStringDelete (langName
);
523 static void printLangugageKindOption (const kindOption
* const kind
)
525 printf (" %c %s%s\n", kind
->letter
,
526 kind
->description
!= NULL
? kind
->description
:
527 (kind
->name
!= NULL
? kind
->name
: ""),
528 kind
->enabled
? "" : " [off]");
531 static void printLangugageKindOptions (const langType language
)
533 const parserDefinition
* lang
;
534 Assert (0 <= language
&& language
< (int) LanguageCount
);
535 lang
= LanguageTable
[language
];
536 if (lang
->kinds
!= NULL
|| lang
->regex
)
539 char* const name
= newLowerString (lang
->name
);
540 printf (" --%s-types=[+|-]kinds\n", name
);
542 if (lang
->kinds
!= NULL
)
543 for (i
= 0 ; i
< lang
->kindCount
; ++i
)
544 printLangugageKindOption (lang
->kinds
+ i
);
546 /*printRegexKindOptions (language);*/ /* unused */
551 extern void printKindOptions (void)
556 "\n The following options are used to specify which language-specific tag\n\
557 types (or kinds) should be included in the tag file. \"Kinds\" is a group of\n\
558 one-letter flags designating kinds of tags to either include or exclude from\n\
559 the output. Each letter or group of letters may be preceded by either '+' to\n\
560 add it to those already included, or '-' to exclude it from the output. In\n\
561 the absence of any preceding '+' or '-' sign, only those kinds listed in\n\
562 \"kinds\" will be included in the output. Below each option is a list of the\n\
563 flags accepted. All kinds are enabled by default unless otherwise noted.\n\n");
565 for (i
= 0 ; i
< LanguageCount
; ++i
)
566 printLangugageKindOptions (i
);
573 static void makeFileTag (const char *const fileName
)
575 if (Option
.include
.fileNames
)
578 initTagEntry (&tag
, baseFilename (fileName
));
580 tag
.isFileEntry
= TRUE
;
581 tag
.lineNumberEntry
= TRUE
;
583 tag
.kindName
= "file";
590 static boolean
createTagsForFile (const char *const fileName
,
591 const langType language
,
592 const unsigned int passCount
)
594 boolean retried
= FALSE
;
596 if (fileOpen (fileName
, language
))
599 makeFileTag (fileName
);
601 if (LanguageTable
[language
]->parser
!= NULL
)
602 LanguageTable
[language
]->parser ();
603 else if (LanguageTable
[language
]->parser2
!= NULL
)
604 retried
= LanguageTable
[language
]->parser2 (passCount
);
613 static boolean
createTagsWithFallback (const char *const fileName
,
614 const langType language
)
616 const unsigned long numTags
= TagFile
.numTags
.added
;
617 MIOPos tagFilePosition
;
618 unsigned int passCount
= 0;
619 boolean tagFileResized
= FALSE
;
621 mio_getpos (TagFile
.mio
, &tagFilePosition
);
622 while (createTagsForFile (fileName
, language
, ++passCount
))
624 /* Restore prior state of tag file.
626 mio_setpos (TagFile
.mio
, &tagFilePosition
);
627 TagFile
.numTags
.added
= numTags
;
628 tagFileResized
= TRUE
;
630 return tagFileResized
;
633 extern boolean
parseFile (const char *const fileName
)
635 boolean tagFileResized
= FALSE
;
636 langType language
= Option
.language
;
637 if (Option
.language
== LANG_AUTO
)
638 language
= getFileLanguage (fileName
);
639 Assert (language
!= LANG_AUTO
);
643 tagFileResized
= createTagsWithFallback (fileName
, language
);
645 addTotals (1, 0L, 0L);
647 return tagFileResized
;
650 /* vi:set tabstop=8 shiftwidth=4 nowrap: */