Update all parsers and related files to ctags p6.1.20240421.0
[geany-mirror.git] / ctags / main / parse.c
blob13c6bef8f7dbb62860d91d35fdede33198087bed
1 /*
2 * Copyright (c) 1996-2003, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for managing input languages and
8 * dispatching files to the appropriate language parser.
9 */
12 * INCLUDE FILES
14 #include "general.h" /* must always come first */
16 /* TODO: This definition should be removed. */
17 #define OPTION_WRITE
18 #include "options_p.h"
20 #include <string.h>
22 #include "ctags.h"
23 #include "debug.h"
24 #include "entry_p.h"
25 #include "field_p.h"
26 #include "flags_p.h"
27 #include "htable.h"
28 #include "keyword.h"
29 #include "lxpath_p.h"
30 #include "param.h"
31 #include "param_p.h"
32 #include "parse_p.h"
33 #include "parsers_p.h"
34 #include "promise.h"
35 #include "promise_p.h"
36 #include "ptag_p.h"
37 #include "ptrarray.h"
38 #include "read.h"
39 #include "read_p.h"
40 #include "routines.h"
41 #include "routines_p.h"
42 #include "stats_p.h"
43 #include "subparser.h"
44 #include "subparser_p.h"
45 #include "trace.h"
46 #include "trashbox.h"
47 #include "trashbox_p.h"
48 #include "vstring.h"
49 #ifdef HAVE_ICONV
50 # include "mbcs_p.h"
51 #endif
52 #include "writer_p.h"
53 #include "xtag_p.h"
56 * DATA TYPES
58 enum specType {
59 SPEC_NONE,
60 SPEC_NAME,
61 SPEC_ALIAS = SPEC_NAME,
62 SPEC_EXTENSION,
63 SPEC_PATTERN,
65 const char *specTypeName [] = {
66 "none", "name", "extension", "pattern"
69 typedef struct {
70 langType lang;
71 const char* spec;
72 enum specType specType;
73 } parserCandidate;
75 typedef struct sParserObject {
76 parserDefinition *def;
78 kindDefinition* fileKind;
80 stringList* currentPatterns; /* current list of file name patterns */
81 stringList* currentExtensions; /* current list of extensions */
82 stringList* currentAliases; /* current list of aliases */
84 unsigned int initialized:1; /* initialize() is called or not */
85 unsigned int dontEmit:1; /* run but don't emit tags.
86 This parser was disabled but a subparser on
87 this parser makes this parser run (to drive
88 the subparser). */
89 unsigned int pseudoTagPrinted:1; /* pseudo tags about this parser
90 is emitted or not. */
91 unsigned int justRunForSchedulingBase:1;
92 unsigned int used; /* Used for printing language specific statistics. */
94 unsigned int anonymousIdentiferId; /* managed by anon* functions */
96 struct slaveControlBlock *slaveControlBlock;
97 struct kindControlBlock *kindControlBlock;
98 struct lregexControlBlock *lregexControlBlock;
99 struct paramControlBlock *paramControlBlock;
101 langType pretendingAsLanguage; /* OLDLANG in --_pretend-<NEWLANG>=<OLDLANG>
102 is set here if this parser is NEWLANG.
103 LANG_IGNORE is set if no pretending. */
104 langType pretendedAsLanguage; /* NEWLANG in --_pretend-<NEWLANG>=<OLDLANG>
105 is set here if this parser is OLDLANG.
106 LANG_IGNORE is set if no being pretended. */
108 } parserObject;
111 * FUNCTION PROTOTYPES
114 static void lazyInitialize (langType language);
115 static void addParserPseudoTags (langType language);
116 static void installKeywordTable (const langType language);
117 static void installTagRegexTable (const langType language);
118 static void installTagXpathTable (const langType language);
119 static void anonResetMaybe (parserObject *parser);
120 static void setupAnon (void);
121 static void teardownAnon (void);
122 static void uninstallTagXpathTable (const langType language);
123 static bool hasLanguageAnyRegexPatterns (const langType language);
126 * DATA DEFINITIONS
128 static parserDefinition *FallbackParser (void);
129 static parserDefinition *CTagsParser (void);
130 static parserDefinition *CTagsSelfTestParser (void);
131 static parserDefinitionFunc* BuiltInParsers[] = {
132 #ifdef EXTERNAL_PARSER_LIST
133 EXTERNAL_PARSER_LIST
134 #else /* ! EXTERNAL_PARSER_LIST */
135 CTagsParser, /* This must be first entry. */
136 FallbackParser, /* LANG_FALLBACK */
137 CTagsSelfTestParser,
139 PARSER_LIST,
140 XML_PARSER_LIST
141 #ifdef HAVE_LIBXML
143 #endif
144 YAML_PARSER_LIST
145 #ifdef HAVE_LIBYAML
147 #endif
148 PEG_PARSER_LIST
149 #ifdef HAVE_PACKCC
151 #endif
152 OPTLIB2C_PCRE2_PARSER_LIST
153 #ifdef HAVE_PCRE2
155 #endif
156 #endif /* EXTERNAL_PARSER_LIST */
158 static parserObject* LanguageTable = NULL;
159 static unsigned int LanguageCount = 0;
160 static hashTable* LanguageHTable = NULL;
161 static kindDefinition defaultFileKind = {
162 .enabled = false,
163 .letter = KIND_FILE_DEFAULT_LETTER,
164 .name = KIND_FILE_DEFAULT_NAME,
165 .description = KIND_FILE_DEFAULT_NAME,
168 static langType ctagsSelfTestLang;
171 * FUNCTION DEFINITIONS
174 static bool isLanguageNameChar(int c)
176 if (isgraph(c))
178 if (c == '\'' || c == '"' || c == ';')
179 return false;
180 return true;
182 else
183 return false;
186 extern unsigned int countParsers (void)
188 return LanguageCount;
191 extern int makeSimpleTag (
192 const vString* const name, const int kindIndex)
194 return makeSimpleRefTag (name, kindIndex, ROLE_DEFINITION_INDEX);
197 extern int makeSimpleRefTag (const vString* const name, const int kindIndex,
198 int roleIndex)
200 int r = CORK_NIL;
202 Assert (roleIndex < (int)countInputLanguageRoles(kindIndex));
204 /* do not check for kind being disabled - that happens later in makeTagEntry() */
205 if (name != NULL && vStringLength (name) > 0)
207 tagEntryInfo e;
208 initRefTagEntry (&e, vStringValue (name), kindIndex, roleIndex);
210 r = makeTagEntry (&e);
212 return r;
215 extern int makeSimplePlaceholder(const vString* const name)
217 return makePlaceholder (vStringValue (name));
220 extern bool isLanguageEnabled (const langType language)
222 const parserDefinition* const def = LanguageTable [language].def;
223 return def->enabled;
226 extern bool isLanguageVisible (const langType language)
228 const parserDefinition* const lang = LanguageTable [language].def;
230 return !lang->invisible;
234 * parserDescription mapping management
237 extern parserDefinition* parserNew (const char* name)
239 parserDefinition* result = xCalloc (1, parserDefinition);
240 result->name = eStrdup (name);
242 result->enabled = true;
243 return result;
246 extern bool doesLanguageAllowNullTag (const langType language)
248 Assert (0 <= language && language < (int) LanguageCount);
249 return LanguageTable [language].def->allowNullTag;
252 extern bool doesLanguageRequestAutomaticFQTag (const langType language)
254 Assert (0 <= language && language < (int) LanguageCount);
255 return LanguageTable [language].def->requestAutomaticFQTag;
258 static const char *getLanguageNameFull (const langType language, bool noPretending)
260 const char* result;
262 if (language == LANG_IGNORE)
263 result = "unknown";
264 else
266 Assert (0 <= language && language < (int) LanguageCount);
267 if (noPretending)
268 result = LanguageTable [language].def->name;
269 else
271 langType real_language = LanguageTable [language].pretendingAsLanguage;
272 if (real_language == LANG_IGNORE)
273 result = LanguageTable [language].def->name;
274 else
276 Assert (0 <= real_language && real_language < (int) LanguageCount);
277 result = LanguageTable [real_language].def->name;
281 return result;
284 extern const char *getLanguageName (const langType language)
286 return getLanguageNameFull (language, false);
289 extern const char *getLanguageKindName (const langType language, const int kindIndex)
291 kindDefinition* kdef = getLanguageKind (language, kindIndex);
292 return kdef->name;
295 static kindDefinition kindGhost = {
296 .letter = KIND_GHOST_LETTER,
297 .name = KIND_GHOST_NAME,
298 .description = KIND_GHOST_NAME,
301 extern int defineLanguageKind (const langType language, kindDefinition *def,
302 freeKindDefFunc freeKindDef)
304 return defineKind (LanguageTable [language].kindControlBlock, def, freeKindDef);
307 extern unsigned int countLanguageKinds (const langType language)
309 return countKinds (LanguageTable [language].kindControlBlock);
312 extern unsigned int countLanguageRoles (const langType language, int kindIndex)
314 return countRoles (LanguageTable [language].kindControlBlock, kindIndex);
317 extern kindDefinition* getLanguageKind (const langType language, int kindIndex)
319 kindDefinition* kdef;
321 Assert (0 <= language && language < (int) LanguageCount);
323 switch (kindIndex)
325 case KIND_FILE_INDEX:
326 kdef = LanguageTable [language].fileKind;
327 break;
328 case KIND_GHOST_INDEX:
329 kdef = &kindGhost;
330 break;
331 default:
332 Assert (kindIndex >= 0);
333 kdef = getKind (LanguageTable [language].kindControlBlock, kindIndex);
335 return kdef;
338 extern kindDefinition* getLanguageKindForLetter (const langType language, char kindLetter)
340 Assert (0 <= language && language < (int) LanguageCount);
341 if (kindLetter == LanguageTable [language].fileKind->letter)
342 return LanguageTable [language].fileKind;
343 else if (kindLetter == KIND_GHOST_LETTER)
344 return &kindGhost;
345 else
346 return getKindForLetter (LanguageTable [language].kindControlBlock, kindLetter);
349 extern kindDefinition* getLanguageKindForName (const langType language, const char *kindName)
351 Assert (0 <= language && language < (int) LanguageCount);
352 Assert (kindName);
354 if (strcmp(kindName, LanguageTable [language].fileKind->name) == 0)
355 return LanguageTable [language].fileKind;
356 else if (strcmp(kindName, KIND_GHOST_NAME) == 0)
357 return &kindGhost;
358 else
359 return getKindForName (LanguageTable [language].kindControlBlock, kindName);
362 extern roleDefinition* getLanguageRole(const langType language, int kindIndex, int roleIndex)
364 return getRole (LanguageTable [language].kindControlBlock, kindIndex, roleIndex);
367 extern roleDefinition* getLanguageRoleForName (const langType language, int kindIndex,
368 const char *roleName)
370 return getRoleForName (LanguageTable [language].kindControlBlock, kindIndex, roleName);
373 extern langType getNamedLanguageFull (const char *const name, size_t len, bool noPretending,
374 bool include_aliases)
376 langType result = LANG_IGNORE;
377 unsigned int i;
378 Assert (name != NULL);
380 if (len == 0)
382 parserDefinition *def = (parserDefinition *)hashTableGetItem (LanguageHTable, name);
383 if (def)
384 result = def->id;
386 else
387 for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
389 const parserDefinition* const lang = LanguageTable [i].def;
390 Assert (lang->name);
391 vString* vstr = vStringNewInit (name);
392 vStringTruncate (vstr, len);
394 if (strcasecmp (vStringValue (vstr), lang->name) == 0)
395 result = i;
396 else if (include_aliases)
398 stringList* const aliases = LanguageTable [i].currentAliases;
399 if (aliases && stringListCaseMatched (aliases, vStringValue (vstr)))
400 result = i;
402 vStringDelete (vstr);
405 if (result != LANG_IGNORE
406 && (!noPretending)
407 && LanguageTable [result].pretendedAsLanguage != LANG_IGNORE)
408 result = LanguageTable [result].pretendedAsLanguage;
410 return result;
413 extern langType getNamedLanguage (const char *const name, size_t len)
415 return getNamedLanguageFull (name, len, false, false);
418 extern langType getNamedLanguageOrAlias (const char *const name, size_t len)
420 return getNamedLanguageFull (name, len, false, true);
423 static langType getNameOrAliasesLanguageAndSpec (const char *const key, langType start_index,
424 const char **const spec, enum specType *specType)
426 langType result = LANG_IGNORE;
427 unsigned int i;
430 if (start_index == LANG_AUTO)
431 start_index = 0;
432 else if (start_index == LANG_IGNORE || start_index >= (int) LanguageCount)
433 return result;
435 for (i = start_index ; i < LanguageCount && result == LANG_IGNORE ; ++i)
437 if (! isLanguageEnabled (i))
438 continue;
440 const parserObject* const parser = LanguageTable + i;
441 stringList* const aliases = parser->currentAliases;
442 vString* tmp;
444 if (parser->def->name != NULL && strcasecmp (key, parser->def->name) == 0)
446 result = i;
447 *spec = parser->def->name;
448 *specType = SPEC_NAME;
450 else if (aliases != NULL && (tmp = stringListFileFinds (aliases, key)))
452 result = i;
453 *spec = vStringValue(tmp);
454 *specType = SPEC_ALIAS;
457 return result;
460 extern langType getLanguageForCommand (const char *const command, langType startFrom)
462 const char *const tmp_command = baseFilename (command);
463 char *tmp_spec;
464 enum specType tmp_specType;
466 return getNameOrAliasesLanguageAndSpec (tmp_command, startFrom,
467 (const char **const)&tmp_spec,
468 &tmp_specType);
471 static langType getPatternLanguageAndSpec (const char *const baseName, langType start_index,
472 const char **const spec, enum specType *specType)
474 langType result = LANG_IGNORE;
475 unsigned int i;
477 if (start_index == LANG_AUTO)
478 start_index = 0;
479 else if (start_index == LANG_IGNORE || start_index >= (int) LanguageCount)
480 return result;
482 *spec = NULL;
483 for (i = start_index ; i < LanguageCount && result == LANG_IGNORE ; ++i)
485 if (! isLanguageEnabled (i))
486 continue;
488 parserObject *parser = LanguageTable + i;
489 stringList* const ptrns = parser->currentPatterns;
490 vString* tmp;
492 if (ptrns != NULL && (tmp = stringListFileFinds (ptrns, baseName)))
494 result = i;
495 *spec = vStringValue(tmp);
496 *specType = SPEC_PATTERN;
497 goto found;
501 for (i = start_index ; i < LanguageCount && result == LANG_IGNORE ; ++i)
503 if (! isLanguageEnabled (i))
504 continue;
506 parserObject *parser = LanguageTable + i;
507 stringList* const exts = parser->currentExtensions;
508 vString* tmp;
510 if (exts != NULL && (tmp = stringListExtensionFinds (exts,
511 fileExtension (baseName))))
513 result = i;
514 *spec = vStringValue(tmp);
515 *specType = SPEC_EXTENSION;
516 goto found;
519 found:
520 return result;
523 extern langType getLanguageForFilename (const char *const filename, langType startFrom)
525 const char *const tmp_filename = baseFilename (filename);
526 char *tmp_spec;
527 enum specType tmp_specType;
529 return getPatternLanguageAndSpec (tmp_filename, startFrom,
530 (const char **const)&tmp_spec,
531 &tmp_specType);
534 const char *scopeSeparatorFor (langType language, int kindIndex, int parentKindIndex)
536 Assert (0 <= language && language < (int) LanguageCount);
538 parserObject *parser = LanguageTable + language;
539 struct kindControlBlock *kcb = parser->kindControlBlock;
541 const scopeSeparator *sep = getScopeSeparator (kcb, kindIndex, parentKindIndex);
542 return sep? sep->separator: NULL;
545 static bool processLangDefineScopesep(const langType language,
546 const char *const option,
547 const char *const parameter)
549 parserObject *parser;
550 const char * p = parameter;
553 char parentKletter;
554 int parentKindex = KIND_FILE_INDEX;
555 char kletter;
556 int kindex = KIND_FILE_INDEX;
557 const char *separator;
559 Assert (0 <= language && language < (int) LanguageCount);
560 parser = LanguageTable + language;
564 * Parent
566 parentKletter = p[0];
568 if (parentKletter == '\0')
569 error (FATAL, "no scope separator specified in \"--%s\" option", option);
570 else if (parentKletter == '/')
571 parentKindex = KIND_GHOST_INDEX;
572 else if (parentKletter == KIND_WILDCARD_LETTER)
573 parentKindex = KIND_WILDCARD_INDEX;
574 else if (parentKletter == KIND_FILE_DEFAULT_LETTER)
575 error (FATAL,
576 "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind and no separator can be assigned to",
577 KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME);
578 else if (isalpha ((unsigned char) parentKletter))
580 kindDefinition *kdef = getKindForLetter (parser->kindControlBlock, parentKletter);
581 if (kdef == NULL)
582 error (FATAL,
583 "the kind for letter `%c' specified in \"--%s\" option is not defined.",
584 parentKletter, option);
585 parentKindex = kdef->id;
587 else
588 error (FATAL,
589 "the kind letter `%c` given in \"--%s\" option is not an alphabet",
590 parentKletter, option);
594 * Child
596 if (parentKindex == KIND_GHOST_INDEX)
597 kletter = p[1];
598 else
600 if (p[1] != '/')
601 error (FATAL,
602 "wrong separator specification in \"--%s\" option: no slash after parent kind letter `%c'",
603 option, parentKletter);
604 kletter = p[2];
607 if (kletter == '\0')
608 error (FATAL, "no child kind letter in \"--%s\" option", option);
609 else if (kletter == '/')
610 error (FATAL,
611 "wrong separator specification in \"--%s\" option: don't specify slash char twice: %s",
612 option, parameter);
613 else if (kletter == ':')
614 error (FATAL,
615 "no child kind letter in \"--%s\" option", option);
616 else if (kletter == KIND_WILDCARD_LETTER)
618 if (parentKindex != KIND_WILDCARD_INDEX
619 && parentKindex != KIND_GHOST_INDEX)
620 error (FATAL,
621 "cannot use wild card for child kind unless parent kind is also wild card or empty");
622 kindex = KIND_WILDCARD_INDEX;
624 else if (kletter == KIND_FILE_DEFAULT_LETTER)
625 error (FATAL,
626 "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind and no separator can be assigned to",
627 KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME);
628 else if (isalpha ((unsigned char) kletter))
630 kindDefinition *kdef = getKindForLetter (parser->kindControlBlock, kletter);
631 if (kdef == NULL)
632 error (FATAL,
633 "the kind for letter `%c' specified in \"--%s\" option is not defined.",
634 kletter, option);
635 kindex = kdef->id;
637 else
638 error (FATAL,
639 "the kind letter `%c` given in \"--%s\" option is not an alphabet",
640 kletter, option);
643 * Separator
645 if (parentKindex == KIND_GHOST_INDEX)
647 if (p[2] != ':')
648 error (FATAL,
649 "wrong separator specification in \"--%s\" option: cannot find a colon after child kind: %s",
650 option, parameter);
651 separator = p + 3;
653 else
655 if (p[3] != ':')
656 error (FATAL,
657 "wrong separator specification in \"--%s\" option: cannot find a colon after child kind: %s",
658 option, parameter);
659 separator = p + 4;
662 Assert (parentKindex != KIND_FILE_INDEX);
663 Assert (kindex != KIND_FILE_INDEX);
664 defineScopeSeparator (parser->kindControlBlock, kindex, parentKindex, separator);
665 return true;
668 extern bool processScopesepOption (const char *const option, const char * const parameter)
670 langType language;
672 language = getLanguageComponentInOption (option, "_scopesep-");
673 if (language == LANG_IGNORE)
674 return false;
676 return processLangDefineScopesep (language, option, parameter);
679 static parserCandidate* parserCandidateNew(unsigned int count CTAGS_ATTR_UNUSED)
681 parserCandidate* candidates;
682 unsigned int i;
684 candidates= xMalloc(LanguageCount, parserCandidate);
685 for (i = 0; i < LanguageCount; i++)
687 candidates[i].lang = LANG_IGNORE;
688 candidates[i].spec = NULL;
689 candidates[i].specType = SPEC_NONE;
691 return candidates;
694 /* If multiple parsers are found, return LANG_AUTO */
695 static unsigned int nominateLanguageCandidates (const char *const key, parserCandidate** candidates)
697 unsigned int count;
698 langType i;
699 const char* spec = NULL;
700 enum specType specType = SPEC_NONE;
702 *candidates = parserCandidateNew(LanguageCount);
704 for (count = 0, i = LANG_AUTO; i != LANG_IGNORE; )
706 i = getNameOrAliasesLanguageAndSpec (key, i, &spec, &specType);
707 if (i != LANG_IGNORE)
709 (*candidates)[count].lang = i++;
710 (*candidates)[count].spec = spec;
711 (*candidates)[count++].specType = specType;
715 return count;
718 static unsigned int
719 nominateLanguageCandidatesForPattern(const char *const baseName, parserCandidate** candidates)
721 unsigned int count;
722 langType i;
723 const char* spec;
724 enum specType specType = SPEC_NONE;
726 *candidates = parserCandidateNew(LanguageCount);
728 for (count = 0, i = LANG_AUTO; i != LANG_IGNORE; )
730 i = getPatternLanguageAndSpec (baseName, i, &spec, &specType);
731 if (i != LANG_IGNORE)
733 (*candidates)[count].lang = i++;
734 (*candidates)[count].spec = spec;
735 (*candidates)[count++].specType = specType;
738 return count;
741 static vString* extractEmacsModeAtFirstLine(MIO* input);
743 /* The name of the language interpreter, either directly or as the argument
744 * to "env".
746 static vString* determineInterpreter (const char* const cmd)
748 vString* const interpreter = vStringNew ();
749 const char* p = cmd;
752 vStringClear (interpreter);
753 for ( ; isspace ((unsigned char) *p) ; ++p)
754 ; /* no-op */
755 for ( ; *p != '\0' && ! isspace ((unsigned char) *p) ; ++p)
756 vStringPut (interpreter, *p);
757 } while (strcmp (vStringValue (interpreter), "env") == 0);
758 return interpreter;
761 static vString* extractInterpreter (MIO* input)
763 vString* const vLine = vStringNew ();
764 const char* const line = readLineRaw (vLine, input);
765 vString* interpreter = NULL;
767 if (line != NULL && line [0] == '#' && line [1] == '!')
769 /* "48.2.4.1 Specifying File Variables" of Emacs info:
770 ---------------------------------------------------
771 In shell scripts, the first line is used to
772 identify the script interpreter, so you
773 cannot put any local variables there. To
774 accommodate this, Emacs looks for local
775 variable specifications in the _second_
776 line if the first line specifies an
777 interpreter. */
779 interpreter = extractEmacsModeAtFirstLine(input);
780 if (!interpreter)
782 const char* const lastSlash = strrchr (line, '/');
783 const char *const cmd = lastSlash != NULL ? lastSlash+1 : line+2;
784 interpreter = determineInterpreter (cmd);
787 vStringDelete (vLine);
788 return interpreter;
791 static bool isShellZsh (const char *p)
793 p = strstr (p, "sh-set-shell");
794 if (!p)
795 return false;
796 p += strlen("sh-set-shell");
798 if (*p == ':')
799 p++;
800 while (isspace ((unsigned char) *p))
801 p++;
803 if (strncmp (p, "\"zsh\"", 5) == 0
804 || strncmp (p, "zsh", 3) == 0)
805 return true;
806 return false;
809 static vString* determineEmacsModeAtFirstLine (const char* const line)
811 vString* mode = vStringNew ();
813 const char* p = strstr(line, "-*-");
814 if (p == NULL)
815 goto out;
816 p += strlen("-*-");
818 for ( ; isspace ((unsigned char) *p) ; ++p)
819 ; /* no-op */
821 if (strncasecmp(p, "mode:", strlen("mode:")) == 0)
823 /* -*- mode: MODE; -*- */
824 p += strlen("mode:");
825 for ( ; isspace ((unsigned char) *p) ; ++p)
826 ; /* no-op */
827 for ( ; *p != '\0' && isLanguageNameChar ((unsigned char) *p) ; ++p)
828 vStringPut (mode, *p);
830 if ((strcmp(vStringValue (mode), "sh") == 0
831 || strcmp(vStringValue (mode), "shell-script") == 0)
832 && isShellZsh (p))
833 vStringCopyS (mode, "Zsh");
835 else
837 /* -*- MODE -*- */
838 const char* end = strstr (p, "-*-");
840 if (end == NULL)
841 goto out;
843 for ( ; p < end && isLanguageNameChar ((unsigned char) *p) ; ++p)
844 vStringPut (mode, *p);
846 for ( ; isspace ((unsigned char) *p) ; ++p)
847 ; /* no-op */
848 if (strncmp(p, "-*-", strlen("-*-")) != 0)
849 vStringClear (mode);
852 vStringLower (mode);
854 out:
855 return mode;
859 static vString* extractEmacsModeAtFirstLine(MIO* input)
861 vString* const vLine = vStringNew ();
862 const char* const line = readLineRaw (vLine, input);
863 vString* mode = NULL;
864 if (line != NULL)
865 mode = determineEmacsModeAtFirstLine (line);
866 vStringDelete (vLine);
868 if (mode && (vStringLength(mode) == 0))
870 vStringDelete(mode);
871 mode = NULL;
873 return mode;
876 static vString* determineEmacsModeAtEOF (MIO* const fp)
878 vString* const vLine = vStringNew ();
879 const char* line;
880 bool headerFound = false;
881 const char* p;
882 vString* mode = vStringNew ();
883 bool is_shell_mode = false;
885 while ((line = readLineRaw (vLine, fp)) != NULL)
887 if (headerFound && ((p = strstr (line, "mode:")) != NULL))
889 vStringClear (mode);
890 headerFound = false;
892 p += strlen ("mode:");
893 for ( ; isspace ((unsigned char) *p) ; ++p)
894 ; /* no-op */
895 for ( ; *p != '\0' && isLanguageNameChar ((unsigned char) *p) ; ++p)
896 vStringPut (mode, *p);
898 is_shell_mode = ((strcasecmp (vStringValue (mode), "sh") == 0
899 || strcasecmp (vStringValue (mode), "shell-script") == 0));
901 else if (headerFound && (p = strstr(line, "End:")))
902 headerFound = false;
903 else if (strstr (line, "Local Variables:"))
904 headerFound = true;
905 else if (is_shell_mode && (p = strstr (line, "sh-set-shell")))
907 p += strlen("sh-set-shell");
908 while (isspace ((unsigned char) *p))
909 p++;
910 if (strncmp (p, "\"zsh\"", 5) == 0)
911 vStringCopyS (mode, "Zsh");
914 vStringDelete (vLine);
915 return mode;
918 static vString* extractEmacsModeLanguageAtEOF (MIO* input)
920 vString* mode;
922 /* "48.2.4.1 Specifying File Variables" of Emacs info:
923 ---------------------------------------------------
924 you can define file local variables using a "local
925 variables list" near the end of the file. The start of the
926 local variables list should be no more than 3000 characters
927 from the end of the file, */
928 mio_seek(input, -3000, SEEK_END);
930 mode = determineEmacsModeAtEOF (input);
931 if (mode && (vStringLength (mode) == 0))
933 vStringDelete (mode);
934 mode = NULL;
937 return mode;
940 static vString* determineVimFileType (const char *const modeline)
942 /* considerable combinations:
943 --------------------------
944 ... filetype=
945 ... ft= */
947 unsigned int i;
948 const char* p;
950 const char* const filetype_prefix[] = {"filetype=", "ft="};
951 vString* const filetype = vStringNew ();
953 for (i = 0; i < ARRAY_SIZE(filetype_prefix); i++)
955 if ((p = strrstr(modeline, filetype_prefix[i])) == NULL)
956 continue;
958 p += strlen(filetype_prefix[i]);
959 for ( ; *p != '\0' && isalnum ((unsigned char) *p) ; ++p)
960 vStringPut (filetype, *p);
961 break;
963 return filetype;
966 static vString* extractVimFileTypeCommon(MIO* input, bool eof)
968 /* http://vimdoc.sourceforge.net/htmldoc/options.html#modeline
970 [text]{white}{vi:|vim:|ex:}[white]se[t] {options}:[text]
971 options=> filetype=TYPE or ft=TYPE
973 'modelines' 'mls' number (default 5)
974 global
975 {not in Vi}
976 If 'modeline' is on 'modelines' gives the number of lines that is
977 checked for set commands. */
979 vString* filetype = NULL;
980 #define RING_SIZE 5
981 vString* ring[RING_SIZE];
982 int i, j;
983 unsigned int k;
984 const char* const prefix[] = {
985 "vim:", "vi:", "ex:"
988 for (i = 0; i < RING_SIZE; i++)
989 ring[i] = vStringNew ();
991 i = 0;
992 while ((readLineRaw (ring[i++], input)) != NULL)
993 if (i == RING_SIZE)
995 i = 0;
996 if (!eof)
998 i++;
999 break;
1003 j = i;
1006 const char* p;
1008 j--;
1009 if (j < 0)
1010 j = RING_SIZE - 1;
1012 for (k = 0; k < ARRAY_SIZE(prefix); k++)
1013 if ((p = strstr (vStringValue (ring[j]), prefix[k])) != NULL)
1015 p += strlen(prefix[k]);
1016 for ( ; isspace ((unsigned char) *p) ; ++p)
1017 ; /* no-op */
1018 filetype = determineVimFileType(p);
1019 break;
1021 } while (((i == RING_SIZE)? (j != RING_SIZE - 1): (j != i)) && (!filetype));
1023 for (i = RING_SIZE - 1; i >= 0; i--)
1024 vStringDelete (ring[i]);
1025 #undef RING_SIZE
1027 if (filetype && (vStringLength (filetype) == 0))
1029 vStringDelete (filetype);
1030 filetype = NULL;
1032 return filetype;
1034 /* TODO:
1035 [text]{white}{vi:|vim:|ex:}[white]{options} */
1038 static vString* extractVimFileTypeAtBOF(MIO* input)
1040 return extractVimFileTypeCommon (input, false);
1043 static vString* extractVimFileTypeAtEOF(MIO* input)
1045 return extractVimFileTypeCommon (input, true);
1048 static vString* extractMarkGeneric (MIO* input,
1049 vString * (* determiner)(const char *const, void *),
1050 void *data)
1052 vString* const vLine = vStringNew ();
1053 const char* const line = readLineRaw (vLine, input);
1054 vString* mode = NULL;
1056 if (line)
1057 mode = determiner (line, data);
1059 vStringDelete (vLine);
1060 return mode;
1063 static vString* determineZshAutoloadTag (const char *const modeline,
1064 void *data CTAGS_ATTR_UNUSED)
1066 /* See "Autoloaded files" in zsh info.
1067 -------------------------------------
1068 #compdef ...
1069 #autoload [ OPTIONS ] */
1071 if (((strncmp (modeline, "#compdef", 8) == 0)
1072 && isspace ((unsigned char) *(modeline + 8)))
1073 || ((strncmp (modeline, "#autoload", 9) == 0)
1074 && (isspace ((unsigned char) *(modeline + 9))
1075 || *(modeline + 9) == '\0')))
1076 return vStringNewInit ("zsh");
1077 else
1078 return NULL;
1081 static vString* extractZshAutoloadTag(MIO* input)
1083 return extractMarkGeneric (input, determineZshAutoloadTag, NULL);
1086 static vString* determinePHPMark(const char *const modeline,
1087 void *data CTAGS_ATTR_UNUSED)
1089 if (strncmp (modeline, "<?php", 5) == 0)
1090 return vStringNewInit ("php");
1091 else
1092 return NULL;
1095 static vString* extractPHPMark(MIO* input)
1097 return extractMarkGeneric (input, determinePHPMark, NULL);
1101 struct getLangCtx {
1102 const char *fileName;
1103 MIO *input;
1104 bool err;
1107 #define GLC_FOPEN_IF_NECESSARY0(_glc_, _label_) do { \
1108 if (!(_glc_)->input) { \
1109 (_glc_)->input = getMio((_glc_)->fileName, "rb", false); \
1110 if (!(_glc_)->input) { \
1111 (_glc_)->err = true; \
1112 goto _label_; \
1115 } while (0) \
1117 #define GLC_FOPEN_IF_NECESSARY(_glc_, _label_, _doesParserRequireMemoryStream_) \
1118 do { \
1119 if (!(_glc_)->input) \
1120 GLC_FOPEN_IF_NECESSARY0 (_glc_, _label_); \
1121 if ((_doesParserRequireMemoryStream_) && \
1122 (mio_memory_get_data((_glc_)->input, NULL) == NULL)) \
1124 MIO *tmp_ = (_glc_)->input; \
1125 (_glc_)->input = mio_new_mio (tmp_, 0, -1); \
1126 mio_unref (tmp_); \
1127 if (!(_glc_)->input) { \
1128 (_glc_)->err = true; \
1129 goto _label_; \
1132 } while (0)
1134 #define GLC_FCLOSE(_glc_) do { \
1135 if ((_glc_)->input) { \
1136 mio_unref((_glc_)->input); \
1137 (_glc_)->input = NULL; \
1139 } while (0)
1141 static const struct taster {
1142 vString* (* taste) (MIO *);
1143 const char *msg;
1144 } eager_tasters[] = {
1146 .taste = extractInterpreter,
1147 .msg = "interpreter",
1150 .taste = extractZshAutoloadTag,
1151 .msg = "zsh autoload tag",
1154 .taste = extractEmacsModeAtFirstLine,
1155 .msg = "emacs mode at the first line",
1158 .taste = extractEmacsModeLanguageAtEOF,
1159 .msg = "emacs mode at the EOF",
1162 .taste = extractVimFileTypeAtBOF,
1163 .msg = "vim modeline at the BOF",
1166 .taste = extractVimFileTypeAtEOF,
1167 .msg = "vim modeline at the EOF",
1170 .taste = extractPHPMark,
1171 .msg = "PHP marker",
1174 static langType tasteLanguage (struct getLangCtx *glc, const struct taster *const tasters, int n_tasters,
1175 langType *fallback);
1177 /* If all the candidates have the same specialized language selector, return
1178 * it. Otherwise, return NULL.
1180 static bool
1181 hasTheSameSelector (langType lang, selectLanguage candidate_selector)
1183 selectLanguage *selector;
1185 selector = LanguageTable[ lang ].def->selectLanguage;
1186 if (selector == NULL)
1187 return false;
1189 while (*selector)
1191 if (*selector == candidate_selector)
1192 return true;
1193 selector++;
1195 return false;
1198 static selectLanguage
1199 commonSelector (const parserCandidate *candidates, int n_candidates)
1201 Assert (n_candidates > 1);
1202 selectLanguage *selector;
1203 int i;
1205 selector = LanguageTable[ candidates[0].lang ].def->selectLanguage;
1206 if (selector == NULL)
1207 return NULL;
1209 while (*selector)
1211 for (i = 1; i < n_candidates; ++i)
1212 if (! hasTheSameSelector (candidates[i].lang, *selector))
1213 break;
1214 if (i == n_candidates)
1215 return *selector;
1216 selector++;
1218 return NULL;
1222 /* Calls the selector and returns the integer value of the parser for the
1223 * language associated with the string returned by the selector.
1225 static int
1226 pickLanguageBySelection (selectLanguage selector, MIO *input,
1227 parserCandidate *candidates,
1228 unsigned int nCandidates)
1230 const char *lang;
1231 langType *cs = xMalloc(nCandidates, langType);
1232 unsigned int i;
1234 for (i = 0; i < nCandidates; i++)
1235 cs[i] = candidates[i].lang;
1236 lang = selector(input, cs, nCandidates);
1237 eFree (cs);
1239 if (lang)
1241 verbose (" selection: %s\n", lang);
1242 return getNamedLanguage(lang, 0);
1244 else
1246 verbose (" no selection\n");
1247 return LANG_IGNORE;
1251 static int compareParsersByName (const void *a, const void* b)
1253 const parserDefinition *const *la = a, *const *lb = b;
1254 return strcasecmp ((*la)->name, (*lb)->name);
1257 static int sortParserCandidatesBySpecType (const void *a, const void *b)
1259 const parserCandidate *ap = a, *bp = b;
1260 if (ap->specType > bp->specType)
1261 return -1;
1262 else if (ap->specType == bp->specType)
1264 /* qsort, the function calling this function,
1265 doesn't do "stable sort". To make the result of
1266 sorting predictable, compare the names of parsers
1267 when their specType is the same. */
1268 parserDefinition *la = LanguageTable [ap->lang].def;
1269 parserDefinition *lb = LanguageTable [bp->lang].def;
1270 return compareParsersByName (&la, &lb);
1272 else
1273 return 1;
1276 static unsigned int sortAndFilterParserCandidates (parserCandidate *candidates,
1277 unsigned int n_candidates)
1279 enum specType highestSpecType;
1280 unsigned int i;
1281 unsigned int r;
1283 if (n_candidates < 2)
1284 return n_candidates;
1286 qsort (candidates, n_candidates, sizeof(*candidates),
1287 sortParserCandidatesBySpecType);
1289 highestSpecType = candidates [0].specType;
1290 r = 1;
1291 for (i = 1; i < n_candidates; i++)
1293 if (candidates[i].specType == highestSpecType)
1294 r++;
1296 return r;
1299 static void verboseReportCandidate (const char *header,
1300 parserCandidate *candidates,
1301 unsigned int n_candidates)
1303 unsigned int i;
1304 verbose (" #%s: %u\n", header, n_candidates);
1305 for (i = 0; i < n_candidates; i++)
1306 verbose (" %u: %s (%s: \"%s\")\n",
1308 LanguageTable[candidates[i].lang].def->name,
1309 specTypeName [candidates[i].specType],
1310 candidates[i].spec);
1313 static bool doesCandidatesRequireMemoryStream(const parserCandidate *candidates,
1314 int n_candidates)
1316 int i;
1318 for (i = 0; i < n_candidates; i++)
1319 if (doesParserRequireMemoryStream (candidates[i].lang))
1320 return true;
1322 return false;
1325 static langType getSpecLanguageCommon (const char *const spec, struct getLangCtx *glc,
1326 unsigned int nominate (const char *const, parserCandidate**),
1327 langType *fallback)
1329 langType language;
1330 parserCandidate *candidates;
1331 unsigned int n_candidates;
1333 if (fallback)
1334 *fallback = LANG_IGNORE;
1336 n_candidates = (*nominate)(spec, &candidates);
1337 verboseReportCandidate ("candidates",
1338 candidates, n_candidates);
1340 n_candidates = sortAndFilterParserCandidates (candidates, n_candidates);
1341 verboseReportCandidate ("candidates after sorting and filtering",
1342 candidates, n_candidates);
1344 if (n_candidates == 1)
1346 language = candidates[0].lang;
1348 else if (n_candidates > 1)
1350 selectLanguage selector = commonSelector(candidates, n_candidates);
1351 bool memStreamRequired = doesCandidatesRequireMemoryStream (candidates,
1352 n_candidates);
1354 GLC_FOPEN_IF_NECESSARY(glc, fopen_error, memStreamRequired);
1355 if (selector) {
1356 verbose (" selector: %p\n", selector);
1357 language = pickLanguageBySelection(selector, glc->input, candidates, n_candidates);
1358 } else {
1359 verbose (" selector: NONE\n");
1360 fopen_error:
1361 language = LANG_IGNORE;
1364 Assert(language != LANG_AUTO);
1366 if (fallback)
1367 *fallback = candidates[0].lang;
1369 else
1371 language = LANG_IGNORE;
1374 eFree(candidates);
1375 candidates = NULL;
1377 return language;
1380 static langType getSpecLanguage (const char *const spec,
1381 struct getLangCtx *glc,
1382 langType *fallback)
1384 return getSpecLanguageCommon(spec, glc, nominateLanguageCandidates,
1385 fallback);
1388 static langType getPatternLanguage (const char *const baseName,
1389 struct getLangCtx *glc,
1390 langType *fallback)
1392 return getSpecLanguageCommon(baseName, glc,
1393 nominateLanguageCandidatesForPattern,
1394 fallback);
1397 /* This function tries to figure out language contained in a file by
1398 * running a series of tests, trying to find some clues in the file.
1400 static langType
1401 tasteLanguage (struct getLangCtx *glc, const struct taster *const tasters, int n_tasters,
1402 langType *fallback)
1404 int i;
1406 if (fallback)
1407 *fallback = LANG_IGNORE;
1408 for (i = 0; i < n_tasters; ++i) {
1409 langType language;
1410 vString* spec;
1412 mio_rewind(glc->input);
1413 spec = tasters[i].taste(glc->input);
1415 if (NULL != spec) {
1416 verbose (" %s: %s\n", tasters[i].msg, vStringValue (spec));
1417 language = getSpecLanguage (vStringValue (spec), glc,
1418 (fallback && (*fallback == LANG_IGNORE))? fallback: NULL);
1419 vStringDelete (spec);
1420 if (language != LANG_IGNORE)
1421 return language;
1425 return LANG_IGNORE;
1429 struct GetLanguageRequest {
1430 enum { GLR_OPEN, GLR_DISCARD, GLR_REUSE, } type;
1431 const char *const fileName;
1432 MIO *mio;
1433 time_t mtime;
1436 static langType
1437 getFileLanguageForRequestInternal (struct GetLanguageRequest *req)
1439 const char *const fileName = req->fileName;
1440 langType language;
1442 /* ctags tries variety ways(HINTS) to choose a proper language
1443 for given fileName. If multiple candidates are chosen in one of
1444 the hint, a SELECTOR common between the candidate languages
1445 is called.
1447 "selection failure" means a selector common between the
1448 candidates doesn't exist or the common selector returns NULL.
1450 "hint failure" means the hint finds no candidate or
1451 "selection failure" occurs though the hint finds multiple
1452 candidates.
1454 If a hint chooses multiple candidates, and selection failure is
1455 occurred, the hint records one of the candidates as FALLBACK for
1456 the hint. (The candidates are stored in an array. The first
1457 element of the array is recorded. However, there is no
1458 specification about the order of elements in the array.)
1460 If all hints are failed, FALLBACKs of the hints are examined.
1461 Which fallbacks should be chosen? `enum hint' defines the order. */
1462 enum hint {
1463 HINT_INTERP,
1464 HINT_OTHER,
1465 HINT_FILENAME,
1466 HINT_TEMPLATE,
1467 N_HINTS,
1469 langType fallback[N_HINTS];
1470 int i;
1471 struct getLangCtx glc = {
1472 .fileName = fileName,
1473 .input = (req->type == GLR_REUSE)? mio_ref (req->mio): NULL,
1474 .err = false,
1476 const char* const baseName = baseFilename (fileName);
1477 char *templateBaseName = NULL;
1478 fileStatus *fstatus = NULL;
1480 for (i = 0; i < N_HINTS; i++)
1481 fallback [i] = LANG_IGNORE;
1483 verbose ("Get file language for %s\n", fileName);
1485 verbose (" pattern: %s\n", baseName);
1486 language = getPatternLanguage (baseName, &glc,
1487 fallback + HINT_FILENAME);
1488 if (language != LANG_IGNORE || glc.err)
1489 goto cleanup;
1492 const char* const tExt = ".in";
1493 templateBaseName = baseFilenameSansExtensionNew (fileName, tExt);
1494 if (templateBaseName)
1496 verbose (" pattern + template(%s): %s\n", tExt, templateBaseName);
1497 GLC_FOPEN_IF_NECESSARY(&glc, cleanup, false);
1498 mio_rewind(glc.input);
1499 language = getPatternLanguage(templateBaseName, &glc,
1500 fallback + HINT_TEMPLATE);
1501 if (language != LANG_IGNORE)
1502 goto cleanup;
1506 /* If the input is already opened, we don't have to verify the existence. */
1507 if (glc.input || ((fstatus = eStat (fileName)) && fstatus->exists))
1509 if ((fstatus && fstatus->isExecutable) || Option.guessLanguageEagerly)
1511 GLC_FOPEN_IF_NECESSARY (&glc, cleanup, false);
1512 language = tasteLanguage(&glc, eager_tasters, 1,
1513 fallback + HINT_INTERP);
1515 if (language != LANG_IGNORE)
1516 goto cleanup;
1518 if (Option.guessLanguageEagerly)
1520 GLC_FOPEN_IF_NECESSARY(&glc, cleanup, false);
1521 language = tasteLanguage(&glc,
1522 eager_tasters + 1,
1523 ARRAY_SIZE(eager_tasters) - 1,
1524 fallback + HINT_OTHER);
1529 cleanup:
1530 if (req->type == GLR_OPEN && glc.input)
1532 req->mio = mio_ref (glc.input);
1533 if (!fstatus)
1534 fstatus = eStat (fileName);
1535 if (fstatus)
1536 req->mtime = fstatus->mtime;
1538 GLC_FCLOSE(&glc);
1539 if (fstatus)
1540 eStatFree (fstatus);
1541 if (templateBaseName)
1542 eFree (templateBaseName);
1544 for (i = 0;
1545 language == LANG_IGNORE && i < N_HINTS;
1546 i++)
1548 language = fallback [i];
1549 if (language != LANG_IGNORE)
1550 verbose (" fallback[hint = %d]: %s\n", i, getLanguageName (language));
1553 if (language == LANG_IGNORE
1554 && isLanguageEnabled (LANG_FALLBACK))
1556 language = LANG_FALLBACK;
1557 verbose (" last resort: using \"%s\" parser\n",
1558 getLanguageName (LANG_FALLBACK));
1560 return language;
1563 static langType getFileLanguageForRequest (struct GetLanguageRequest *req)
1565 langType l = Option.language;
1567 if (l == LANG_AUTO)
1568 return getFileLanguageForRequestInternal(req);
1569 else if (! isLanguageEnabled (l))
1571 error (FATAL,
1572 "%s parser specified with --language-force is disabled",
1573 getLanguageName (l));
1574 /* For suppressing warnings. */
1575 return LANG_AUTO;
1577 else
1578 return Option.language;
1581 extern langType getLanguageForFilenameAndContents (const char *const fileName)
1583 struct GetLanguageRequest req = {
1584 .type = GLR_DISCARD,
1585 .fileName = fileName,
1586 .mtime = (time_t)0,
1589 return getFileLanguageForRequest (&req);
1592 typedef void (*languageCallback) (langType language, void* user_data);
1593 static void foreachLanguage(languageCallback callback, void *user_data)
1595 langType result = LANG_IGNORE;
1597 unsigned int i;
1598 for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
1600 const parserDefinition* const lang = LanguageTable [i].def;
1601 if (lang->name != NULL)
1602 callback(i, user_data);
1606 static void printLanguageMap (const langType language, FILE *fp)
1608 bool first = true;
1609 unsigned int i;
1610 parserObject *parser = LanguageTable + language;
1611 stringList* map = parser->currentPatterns;
1612 Assert (0 <= language && language < (int) LanguageCount);
1613 for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
1615 fprintf (fp, "%s(%s)", (first ? "" : " "),
1616 vStringValue (stringListItem (map, i)));
1617 first = false;
1619 map = parser->currentExtensions;
1620 for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
1622 fprintf (fp, "%s.%s", (first ? "" : " "),
1623 vStringValue (stringListItem (map, i)));
1624 first = false;
1628 extern void installLanguageMapDefault (const langType language)
1630 parserObject* parser;
1631 Assert (0 <= language && language < (int) LanguageCount);
1632 parser = LanguageTable + language;
1633 if (parser->currentPatterns != NULL)
1634 stringListDelete (parser->currentPatterns);
1635 if (parser->currentExtensions != NULL)
1636 stringListDelete (parser->currentExtensions);
1638 if (parser->def->patterns == NULL)
1639 parser->currentPatterns = stringListNew ();
1640 else
1642 parser->currentPatterns =
1643 stringListNewFromArgv (parser->def->patterns);
1645 if (parser->def->extensions == NULL)
1646 parser->currentExtensions = stringListNew ();
1647 else
1649 parser->currentExtensions =
1650 stringListNewFromArgv (parser->def->extensions);
1652 BEGIN_VERBOSE(vfp);
1654 printLanguageMap (language, vfp);
1655 putc ('\n', vfp);
1657 END_VERBOSE();
1660 extern void installLanguageMapDefaults (void)
1662 unsigned int i;
1663 for (i = 0 ; i < LanguageCount ; ++i)
1665 verbose (" %s: ", getLanguageName (i));
1666 installLanguageMapDefault (i);
1670 extern void installLanguageAliasesDefault (const langType language)
1672 parserObject* parser;
1673 Assert (0 <= language && language < (int) LanguageCount);
1674 parser = LanguageTable + language;
1675 if (parser->currentAliases != NULL)
1676 stringListDelete (parser->currentAliases);
1678 if (parser->def->aliases == NULL)
1679 parser->currentAliases = stringListNew ();
1680 else
1682 parser->currentAliases =
1683 stringListNewFromArgv (parser->def->aliases);
1685 BEGIN_VERBOSE(vfp);
1686 if (parser->currentAliases != NULL)
1687 for (unsigned int i = 0 ; i < stringListCount (parser->currentAliases) ; ++i)
1688 fprintf (vfp, " %s", vStringValue (
1689 stringListItem (parser->currentAliases, i)));
1690 putc ('\n', vfp);
1691 END_VERBOSE();
1694 extern void installLanguageAliasesDefaults (void)
1696 unsigned int i;
1697 for (i = 0 ; i < LanguageCount ; ++i)
1699 verbose (" %s: ", getLanguageName (i));
1700 installLanguageAliasesDefault (i);
1704 extern void clearLanguageMap (const langType language)
1706 Assert (0 <= language && language < (int) LanguageCount);
1707 stringListClear ((LanguageTable + language)->currentPatterns);
1708 stringListClear ((LanguageTable + language)->currentExtensions);
1711 extern void clearLanguageAliases (const langType language)
1713 Assert (0 <= language && language < (int) LanguageCount);
1715 parserObject* parser = (LanguageTable + language);
1716 if (parser->currentAliases)
1717 stringListClear (parser->currentAliases);
1720 static bool removeLanguagePatternMap1(const langType language, const char *const pattern)
1722 bool result = false;
1723 stringList* const ptrn = (LanguageTable + language)->currentPatterns;
1725 if (ptrn != NULL && stringListDeleteItemExtension (ptrn, pattern))
1727 verbose (" (removed from %s)", getLanguageName (language));
1728 result = true;
1730 return result;
1733 extern bool removeLanguagePatternMap (const langType language, const char *const pattern)
1735 bool result = false;
1737 if (language == LANG_AUTO)
1739 unsigned int i;
1740 for (i = 0 ; i < LanguageCount && ! result ; ++i)
1741 result = removeLanguagePatternMap1 (i, pattern) || result;
1743 else
1744 result = removeLanguagePatternMap1 (language, pattern);
1745 return result;
1748 extern void addLanguagePatternMap (const langType language, const char* ptrn,
1749 bool exclusiveInAllLanguages)
1751 vString* const str = vStringNewInit (ptrn);
1752 parserObject* parser;
1753 Assert (0 <= language && language < (int) LanguageCount);
1754 parser = LanguageTable + language;
1755 if (exclusiveInAllLanguages)
1756 removeLanguagePatternMap (LANG_AUTO, ptrn);
1757 stringListAdd (parser->currentPatterns, str);
1760 static bool removeLanguageExtensionMap1 (const langType language, const char *const extension)
1762 bool result = false;
1763 stringList* const exts = (LanguageTable + language)->currentExtensions;
1765 if (exts != NULL && stringListDeleteItemExtension (exts, extension))
1767 verbose (" (removed from %s)", getLanguageName (language));
1768 result = true;
1770 return result;
1773 extern bool removeLanguageExtensionMap (const langType language, const char *const extension)
1775 bool result = false;
1777 if (language == LANG_AUTO)
1779 unsigned int i;
1780 for (i = 0 ; i < LanguageCount ; ++i)
1781 result = removeLanguageExtensionMap1 (i, extension) || result;
1783 else
1784 result = removeLanguageExtensionMap1 (language, extension);
1785 return result;
1788 extern void addLanguageExtensionMap (
1789 const langType language, const char* extension,
1790 bool exclusiveInAllLanguages)
1792 vString* const str = vStringNewInit (extension);
1793 Assert (0 <= language && language < (int) LanguageCount);
1794 if (exclusiveInAllLanguages)
1795 removeLanguageExtensionMap (LANG_AUTO, extension);
1796 stringListAdd ((LanguageTable + language)->currentExtensions, str);
1799 extern void addLanguageAlias (const langType language, const char* alias)
1801 vString* const str = vStringNewInit (alias);
1802 parserObject* parser;
1803 Assert (0 <= language && language < (int) LanguageCount);
1804 parser = LanguageTable + language;
1805 if (parser->currentAliases == NULL)
1806 parser->currentAliases = stringListNew ();
1807 stringListAdd (parser->currentAliases, str);
1810 extern void enableLanguage (const langType language, const bool state)
1812 Assert (0 <= language && language < (int) LanguageCount);
1813 LanguageTable [language].def->enabled = state;
1816 #ifdef DO_TRACING
1817 extern void traceLanguage (langType language)
1819 Assert (0 <= language && language < (int) LanguageCount);
1820 LanguageTable [language].def->traced = true;
1822 extern bool isLanguageTraced (langType language)
1824 Assert (0 <= language && language < (int) LanguageCount);
1825 return LanguageTable [language].def->traced;
1827 #endif /* DO_TRACING */
1829 extern void enableLanguages (const bool state)
1831 unsigned int i;
1832 for (i = 0 ; i < LanguageCount ; ++i)
1833 enableLanguage (i, state);
1836 static void installFieldDefinition (const langType language)
1838 unsigned int i;
1839 parserDefinition * parser;
1841 Assert (0 <= language && language < (int) LanguageCount);
1842 parser = LanguageTable [language].def;
1844 if (parser->fieldTable != NULL)
1846 for (i = 0; i < parser->fieldCount; i++)
1847 defineField (& parser->fieldTable [i], language);
1851 static void installXtagDefinition (const langType language)
1853 unsigned int i;
1854 parserDefinition * parser;
1856 Assert (0 <= language && language < (int) LanguageCount);
1857 parser = LanguageTable [language].def;
1859 if (parser->xtagTable != NULL)
1861 for (i = 0; i < parser->xtagCount; i++)
1862 defineXtag (& parser->xtagTable [i], language);
1866 static void initializeParserOne (langType lang)
1868 parserObject *const parser = LanguageTable + lang;
1870 if (parser->initialized)
1871 goto out;
1873 verbose ("Initialize parser: %s\n", parser->def->name);
1874 parser->initialized = true;
1876 installKeywordTable (lang);
1877 installTagXpathTable (lang);
1878 installFieldDefinition (lang);
1879 installXtagDefinition (lang);
1881 /* regex definitions refers xtag definitions.
1882 So installing RegexTable must be after installing
1883 xtag definitions. */
1884 installTagRegexTable (lang);
1886 if (parser->def->initialize != NULL)
1887 parser->def->initialize (lang);
1889 initializeDependencies (parser->def, parser->slaveControlBlock);
1891 Assert (parser->fileKind != NULL);
1892 Assert (!doesParserUseKind (parser->kindControlBlock, parser->fileKind->letter));
1894 return;
1896 out:
1897 /* lazyInitialize() installs findRegexTags() to parser->parser.
1898 findRegexTags() should be installed to a parser if the parser is
1899 optlib based(created by --langdef) and has some regex patterns(defined
1900 with --regex-<LANG>). findRegexTags() makes regex matching work.
1902 If a parser can be initialized during evaluating options,
1903 --fields-<LANG>=+{something}, for an example.
1904 If such option is evaluated first, evaluating --regex-<LANG>=...
1905 option doesn't cause installing findRegexTags. As the result
1906 regex matching doesn't work. lazyInitialize was called only
1907 once when --fields-<LANG>=+{something} was evaluated. In the
1908 timing ctags had not seen --regex-<LANG>=.... Even though
1909 ctags saw --regex-<LANG>=.... after initializing, there
1910 was no chance to install findRegexTags() to parser->parser.
1912 Following code block gives extra chances to call lazyInitialize)
1913 which installs findRegexTags() to parser->parser. */
1914 if (parser->def->initialize == lazyInitialize)
1915 parser->def->initialize (lang);
1918 extern void initializeParser (langType lang)
1920 if (lang == LANG_AUTO)
1922 unsigned int i;
1923 for (i = 0; i < countParsers(); i++)
1924 initializeParserOne (i);
1926 else
1927 initializeParserOne (lang);
1930 static void linkDependenciesAtInitializeParsing (parserDefinition *const parser)
1932 unsigned int i;
1933 parserDependency *d;
1934 langType upper;
1935 parserDefinition *lowerParser;
1936 parserObject *upperParser;
1938 for (i = 0; i < parser->dependencyCount; i++)
1940 d = parser->dependencies + i;
1942 if (d->type == DEPTYPE_FOREIGNER)
1944 upper = parser->id;
1945 langType lower = getNamedLanguage (d->upperParser, 0);
1946 if (lower == LANG_IGNORE)
1947 error (FATAL,
1948 "Unknown language: \"%s\" as a foreigner for %s",
1949 d->upperParser, parser->name);
1951 lowerParser = LanguageTable [lower].def;
1953 else
1955 upper = getNamedLanguage (d->upperParser, 0);
1956 lowerParser = parser;
1959 upperParser = LanguageTable + upper;
1961 verbose ("link dependencies: type = %s, upper = %s, lower = %s\n",
1962 dependencyTypeString(d->type),
1963 upperParser->def->name, lowerParser->name);
1964 linkDependencyAtInitializeParsing (d->type, upperParser->def,
1965 upperParser->slaveControlBlock,
1966 upperParser->kindControlBlock,
1967 lowerParser,
1968 (LanguageTable + lowerParser->id)->kindControlBlock,
1969 d->data);
1973 /* Used in both builtin and optlib parsers. */
1974 static void initializeParsingCommon (parserDefinition *def, bool is_builtin)
1976 parserObject *parser;
1978 if (is_builtin)
1979 verbose ("%s%s", LanguageCount > 0 ? ", " : "", def->name);
1980 else
1981 verbose ("Add optlib parser: %s\n", def->name);
1983 def->id = LanguageCount++;
1984 parser = LanguageTable + def->id;
1985 parser->def = def;
1987 hashTablePutItem (LanguageHTable, def->name, def);
1989 parser->fileKind = &defaultFileKind;
1991 parser->kindControlBlock = allocKindControlBlock (def);
1992 parser->slaveControlBlock = allocSlaveControlBlock (def);
1993 parser->lregexControlBlock = allocLregexControlBlock (def);
1994 parser->paramControlBlock = allocParamControlBlock (def);
1997 static char *acceptableLangName(char *name)
1999 for (char *c = name; *c != '\0'; c++)
2001 if (isalnum ((unsigned char)*c))
2002 continue;
2003 else if (*c == '+' || *c == '#')
2004 continue;
2005 else
2006 return c;
2008 return NULL;
2011 extern void initializeParsing (void)
2013 unsigned int builtInCount;
2014 unsigned int i;
2016 builtInCount = ARRAY_SIZE (BuiltInParsers);
2017 LanguageTable = xMalloc (builtInCount, parserObject);
2018 memset(LanguageTable, 0, builtInCount * sizeof (parserObject));
2019 for (i = 0; i < builtInCount; ++i)
2021 LanguageTable [i].pretendingAsLanguage = LANG_IGNORE;
2022 LanguageTable [i].pretendedAsLanguage = LANG_IGNORE;
2025 LanguageHTable = hashTableNew (127,
2026 hashCstrcasehash,
2027 hashCstrcaseeq,
2028 NULL,
2029 NULL);
2030 DEFAULT_TRASH_BOX(LanguageHTable, hashTableDelete);
2032 verbose ("Installing parsers: ");
2033 for (i = 0 ; i < builtInCount ; ++i)
2035 parserDefinition* const def = (*BuiltInParsers [i]) ();
2036 if (def != NULL)
2038 Assert (def->name);
2039 Assert (def->name[0] != '\0');
2040 Assert (strcmp (def->name, RSV_LANG_ALL));
2041 Assert (acceptableLangName (def->name) == NULL);
2043 if (def->method & METHOD_NOT_CRAFTED)
2044 def->parser = findRegexTags;
2045 else
2046 /* parser definition must define one and only one parsing routine */
2047 Assert ((!!def->parser) + (!!def->parser2) == 1);
2049 initializeParsingCommon (def, true);
2052 verbose ("\n");
2054 for (i = 0; i < builtInCount ; ++i)
2055 linkDependenciesAtInitializeParsing (LanguageTable [i].def);
2058 extern void freeParserResources (void)
2060 unsigned int i;
2061 for (i = 0 ; i < LanguageCount ; ++i)
2063 parserObject* const parser = LanguageTable + i;
2065 if (parser->def->finalize)
2066 (parser->def->finalize)((langType)i, (bool)parser->initialized);
2068 uninstallTagXpathTable (i);
2070 freeLregexControlBlock (parser->lregexControlBlock);
2071 freeKindControlBlock (parser->kindControlBlock);
2072 parser->kindControlBlock = NULL;
2074 finalizeDependencies (parser->def, parser->slaveControlBlock);
2075 freeSlaveControlBlock (parser->slaveControlBlock);
2076 parser->slaveControlBlock = NULL;
2078 freeParamControlBlock (parser->paramControlBlock);
2080 freeList (&parser->currentPatterns);
2081 freeList (&parser->currentExtensions);
2082 freeList (&parser->currentAliases);
2084 eFree (parser->def->name);
2085 parser->def->name = NULL;
2086 eFree (parser->def);
2087 parser->def = NULL;
2089 if (LanguageTable != NULL)
2090 eFree (LanguageTable);
2091 LanguageTable = NULL;
2092 LanguageCount = 0;
2095 static void doNothing (void)
2099 static void optlibRunBaseParser (void)
2101 scheduleRunningBaseparser (0);
2104 static bool optlibIsDedicatedSubparser (parserDefinition* def)
2106 return (def->dependencies
2107 && (def->dependencies->type == DEPTYPE_SUBPARSER)
2108 && ((subparser *)def->dependencies->data)->direction & SUBPARSER_SUB_RUNS_BASE);
2111 static void lazyInitialize (langType language)
2113 parserDefinition* def;
2115 Assert (0 <= language && language < (int) LanguageCount);
2116 def = LanguageTable [language].def;
2118 def->parser = doNothing;
2120 if (def->method & METHOD_REGEX)
2122 if (optlibIsDedicatedSubparser (def))
2123 def->parser = optlibRunBaseParser;
2124 else
2125 def->parser = findRegexTags;
2129 extern void enableDefaultFileKind (bool state)
2131 defaultFileKind.enabled = state;
2135 * Option parsing
2137 struct preLangDefFlagData
2139 const char *const name;
2140 char *base;
2141 subparserRunDirection direction;
2142 bool autoFQTag;
2143 intArray *foreignLanguages;
2144 unsigned int versionCurrent;
2145 unsigned int versionAge;
2148 static void pre_lang_def_flag_base_long (const char* const optflag, const char* const param, void* data)
2150 struct preLangDefFlagData * flag_data = data;
2151 langType base;
2153 if (param[0] == '\0')
2155 error (WARNING, "No base parser specified for \"%s\" flag of --langdef option", optflag);
2156 return;
2159 base = getNamedLanguage (param, 0);
2160 if (base == LANG_IGNORE)
2162 error (WARNING, "Unknown language(%s) is specified for \"%s\" flag of --langdef option",
2163 param, optflag);
2164 return;
2168 langType cpreproc = getNamedLanguage ("CPreProcessor", 0);
2169 if (base == cpreproc)
2171 /* See Tmain/optscript-preludes-stack.d */
2172 error (WARNING,
2173 "Because of an internal limitation, Making a sub parser based on the CPreProcessor parser is not allowed: %s",
2174 param);
2175 return;
2178 flag_data->base = eStrdup(param);
2181 #define LANGDEF_FLAG_DEDICATED "dedicated"
2182 #define LANGDEF_FLAG_SHARED "shared"
2183 #define LANGDEF_FLAG_BIDIR "bidirectional"
2184 static void pre_lang_def_flag_direction_long (const char* const optflag, const char* const param CTAGS_ATTR_UNUSED, void* data)
2186 struct preLangDefFlagData * flag_data = data;
2188 if (strcmp(optflag, LANGDEF_FLAG_DEDICATED) == 0)
2189 flag_data->direction = SUBPARSER_SUB_RUNS_BASE;
2190 else if (strcmp(optflag, LANGDEF_FLAG_SHARED) == 0)
2191 flag_data->direction = SUBPARSER_BASE_RUNS_SUB;
2192 else if (strcmp(optflag, LANGDEF_FLAG_BIDIR) == 0)
2193 flag_data->direction = SUBPARSER_BI_DIRECTION;
2194 else
2195 AssertNotReached ();
2198 static void pre_lang_def_flag_autoFQTag_long (const char* const optflag,
2199 const char* const param CTAGS_ATTR_UNUSED,
2200 void* data)
2202 struct preLangDefFlagData * flag_data = data;
2203 flag_data->autoFQTag = true;
2206 static void pre_lang_def_flag_foreignLanguage_long (const char* const optflag,
2207 const char* const param,
2208 void* data)
2210 struct preLangDefFlagData * flag_data = data;
2211 if (!param)
2213 error (WARNING, "value for '%s' flag is empty", optflag);
2214 return;
2217 langType lang = getNamedLanguage (param, 0);
2218 if (lang == LANG_IGNORE)
2219 error (FATAL, "language named '%s' is not found or not initialized yet",
2220 param);
2222 verbose ("Foreign language for %s: %s\n", flag_data->name, getLanguageName (lang));
2223 intArrayAdd (flag_data->foreignLanguages, lang);
2226 static void pre_lang_def_flag_version_long (const char* const optflag CTAGS_ATTR_UNUSED,
2227 const char* const param,
2228 void* data)
2230 struct preLangDefFlagData * flag_data = data;
2231 char * verstr = eStrdup (param);
2232 char * age = strchr(verstr, '.');
2233 if (!age)
2234 error (FATAL, "Faile to parse the version number ('.') for language \"%s\": %s",
2235 flag_data->name, param);
2236 *age = '\0';
2237 age++;
2239 if (!strToUInt (verstr, 10, &flag_data->versionCurrent))
2240 error (FATAL, "Faile to parse the version number (the current part) for language \"%s\": %s",
2241 flag_data->name, param);
2243 if (!strToUInt (age, 10, &flag_data->versionAge))
2244 error (FATAL, "Faile to parse the version number (the age part) for language \"%s\": %s",
2245 flag_data->name, param);
2247 eFree (verstr);
2250 static flagDefinition PreLangDefFlagDef [] = {
2251 { '\0', "base", NULL, pre_lang_def_flag_base_long,
2252 "BASEPARSER", "utilize as a base parser"},
2253 { '\0', LANGDEF_FLAG_DEDICATED, NULL,
2254 pre_lang_def_flag_direction_long,
2255 NULL, "make the base parser dedicated to this subparser"},
2256 { '\0', LANGDEF_FLAG_SHARED, NULL,
2257 pre_lang_def_flag_direction_long,
2258 NULL, "share the base parser with the other subparsers"
2260 { '\0', LANGDEF_FLAG_BIDIR, NULL,
2261 pre_lang_def_flag_direction_long,
2262 NULL, "utilize the base parser both 'dedicated' and 'shared' way"
2264 { '\0', "_autoFQTag", NULL, pre_lang_def_flag_autoFQTag_long,
2265 NULL, "make full qualified tags automatically based on scope information"},
2266 { '\0', "_foreignLanguage", NULL, pre_lang_def_flag_foreignLanguage_long,
2267 "LANG", "initialize another parser" },
2268 { '\0', "version", NULL, pre_lang_def_flag_version_long,
2269 NULL, "set the version of the parser (current.age)"},
2272 static void optlibFreeDep (langType lang, bool initialized CTAGS_ATTR_UNUSED)
2274 parserDefinition * pdef = LanguageTable [lang].def;
2276 for (size_t i = 0; i < pdef->dependencyCount; i++)
2278 parserDependency *dep = pdef->dependencies + i;
2280 eFree ((char *)dep->upperParser); /* Dirty cast */
2281 dep->upperParser = NULL;
2283 if (dep->data)
2285 eFree (dep->data);
2286 dep->data = NULL;
2289 if (pdef->dependencies)
2291 eFree (pdef->dependencies);
2292 pdef->dependencies = NULL;
2296 static parserDefinition* OptlibParser(const char *name, const char *base,
2297 subparserRunDirection direction,
2298 intArray *foreignLanguages)
2300 parserDefinition *def;
2301 parserDependency *dep = NULL;
2303 def = parserNew (name);
2304 def->initialize = lazyInitialize;
2305 def->method = METHOD_NOT_CRAFTED;
2307 size_t dep_count = (base? 1: 0) + intArrayCount (foreignLanguages);
2309 if (dep_count)
2311 dep = xCalloc (dep_count, parserDependency);
2312 def->dependencies = dep;
2313 def->dependencyCount = dep_count;
2314 def->finalize = optlibFreeDep;
2317 if (base)
2319 subparser *sub = xCalloc (1, subparser);
2321 sub->direction = direction;
2322 dep[0].type = DEPTYPE_SUBPARSER;
2323 dep[0].upperParser = eStrdup (base);
2324 dep[0].data = sub;
2327 for (size_t i = 0 ; i < intArrayCount (foreignLanguages); i++)
2329 size_t index = (base? 1: 0) + i;
2330 langType lang = intArrayItem (foreignLanguages, i);
2331 Assert (lang != LANG_IGNORE
2332 && lang != LANG_AUTO
2333 && lang < (int) LanguageCount);
2335 dep[index].type = DEPTYPE_FOREIGNER;
2336 dep[index].upperParser = eStrdup (getLanguageName (lang));
2337 dep[index].data = NULL;
2340 return def;
2343 extern void processLanguageDefineOption (
2344 const char *const option, const char *const parameter)
2346 char *name;
2347 char *flags;
2348 parserDefinition* def;
2350 flags = strchr (parameter, LONG_FLAGS_OPEN);
2351 if (flags)
2352 name = eStrndup (parameter, flags - parameter);
2353 else
2354 name = eStrdup (parameter);
2356 /* Veirfy that the name of new language is acceptable or not. */
2357 char *unacceptable;
2358 if (name [0] == '\0')
2360 eFree (name);
2361 error (FATAL, "No language specified for \"%s\" option", option);
2363 else if (getNamedLanguage (name, 0) != LANG_IGNORE)
2365 /* name cannot be freed because it is used in the FATAL message. */
2366 error (FATAL, "Language \"%s\" already defined", name);
2368 else if (strcmp(name, RSV_LANG_ALL) == 0)
2370 eFree (name);
2371 error (FATAL, "\"" RSV_LANG_ALL "\" is reserved; don't use it as the name for defining a new language");
2373 else if (strcmp(name, RSV_NONE) == 0)
2375 eFree (name);
2376 error (FATAL, "\"" RSV_NONE "\" is reserved; don't use it as the name for defining a new language");
2379 else if ((unacceptable = acceptableLangName(name)))
2381 char c = *unacceptable;
2383 /* name cannot be freed because it is used in the FATAL message. */
2384 /* We accept '_'.
2385 * We accept # and + because they are already used in C# parser and C++ parser.
2386 * {... is already trimmed at the beginning of this function. */
2387 if ((c == '`') || (c == '\''))
2388 error (FATAL, "don't use \"%c\" in a language name (%s)", c, name);
2389 else
2390 error (FATAL, "don't use `%c' in a language name (%s)", c, name);
2393 LanguageTable = xRealloc (LanguageTable, LanguageCount + 1, parserObject);
2394 memset (LanguageTable + LanguageCount, 0, sizeof(parserObject));
2396 struct preLangDefFlagData data = {
2397 .name = name,
2398 .base = NULL,
2399 .direction = SUBPARSER_UNKNOWN_DIRECTION,
2400 .autoFQTag = false,
2401 .versionCurrent = 0,
2402 .versionAge = 0,
2404 data.foreignLanguages = intArrayNew ();
2406 flagsEval (flags, PreLangDefFlagDef, ARRAY_SIZE (PreLangDefFlagDef), &data);
2408 if (data.base == NULL && data.direction != SUBPARSER_UNKNOWN_DIRECTION)
2409 error (WARNING, "Ignore the direction of subparser because \"{base=}\" is not given");
2411 if (data.base && data.direction == SUBPARSER_UNKNOWN_DIRECTION)
2412 data.direction = SUBPARSER_BASE_RUNS_SUB;
2414 def = OptlibParser (name, data.base, data.direction,
2415 data.foreignLanguages);
2416 if (data.base)
2417 eFree (data.base);
2419 def->requestAutomaticFQTag = data.autoFQTag;
2420 def->versionCurrent = data.versionCurrent;
2421 def->versionAge = data.versionAge;
2423 initializeParsingCommon (def, false);
2424 linkDependenciesAtInitializeParsing (def);
2426 LanguageTable [def->id].currentPatterns = stringListNew ();
2427 LanguageTable [def->id].currentExtensions = stringListNew ();
2428 LanguageTable [def->id].pretendingAsLanguage = LANG_IGNORE;
2429 LanguageTable [def->id].pretendedAsLanguage = LANG_IGNORE;
2431 intArrayDelete (data.foreignLanguages);
2432 eFree (name);
2435 extern bool doesLanguageHaveForeignDependency (const langType lang,
2436 const langType foreignLang)
2438 Assert (lang != LANG_IGNORE
2439 && lang != LANG_AUTO
2440 && lang < (int) LanguageCount);
2441 Assert (foreignLang != LANG_IGNORE
2442 && foreignLang != LANG_AUTO
2443 && foreignLang < (int) LanguageCount);
2446 parserDefinition * pdef = LanguageTable [lang].def;
2448 for (size_t i = 0; i < pdef->dependencyCount; i++)
2450 parserDependency *dep = pdef->dependencies + i;
2452 if (dep->type == DEPTYPE_FOREIGNER)
2454 if (getNamedLanguage (dep->upperParser, 0) == foreignLang)
2455 return true;
2459 return false;
2462 extern bool isLanguageKindEnabled (const langType language, int kindIndex)
2464 kindDefinition * kdef = getLanguageKind (language, kindIndex);
2465 return kdef->enabled;
2468 extern bool isLanguageRoleEnabled (const langType language, int kindIndex, int roleIndex)
2470 return isRoleEnabled(LanguageTable [language].kindControlBlock,
2471 kindIndex, roleIndex);
2474 extern bool isLanguageKindRefOnly (const langType language, int kindIndex)
2476 kindDefinition * def = getLanguageKind(language, kindIndex);
2477 return def->referenceOnly;
2480 static void resetLanguageKinds (const langType language, const bool mode)
2482 const parserObject* parser;
2484 Assert (0 <= language && language < (int) LanguageCount);
2485 parser = LanguageTable + language;
2488 unsigned int i;
2489 struct kindControlBlock *kcb = parser->kindControlBlock;
2491 for (i = 0 ; i < countKinds (kcb) ; ++i)
2493 kindDefinition *kdef = getKind (kcb, i);
2494 enableKind (kdef, mode);
2499 static bool enableLanguageKindForLetter (
2500 const langType language, const int kind, const bool mode)
2502 bool result = false;
2503 kindDefinition* const def = getLanguageKindForLetter (language, kind);
2504 if (def != NULL)
2506 enableKind (def, mode);
2507 result = true;
2509 return result;
2512 static bool enableLanguageKindForName (
2513 const langType language, const char * const name, const bool mode)
2515 bool result = false;
2516 kindDefinition* const def = getLanguageKindForName (language, name);
2517 if (def != NULL)
2519 enableKind (def, mode);
2520 result = true;
2522 return result;
2525 static void processLangKindDefinition (
2526 const langType language, const char *const option,
2527 const char *const parameter)
2529 const char *p = parameter;
2530 bool mode = true;
2531 int c;
2532 static vString *longName;
2533 bool inLongName = false;
2534 const char *k;
2535 bool r;
2537 Assert (0 <= language && language < (int) LanguageCount);
2539 initializeParser (language);
2540 if (*p == '*')
2542 resetLanguageKinds (language, true);
2543 p++;
2545 else if (*p != '+' && *p != '-')
2546 resetLanguageKinds (language, false);
2548 longName = vStringNewOrClearWithAutoRelease (longName);
2550 while ((c = (unsigned char) *p++) != '\0')
2552 switch (c)
2554 case '+':
2555 if (inLongName)
2556 vStringPut (longName, c);
2557 else
2558 mode = true;
2559 break;
2560 case '-':
2561 if (inLongName)
2562 vStringPut (longName, c);
2563 else
2564 mode = false;
2565 break;
2566 case '{':
2567 if (inLongName)
2568 error(FATAL,
2569 "unexpected character in kind specification: \'%c\'",
2571 inLongName = true;
2572 break;
2573 case '}':
2574 if (!inLongName)
2575 error(FATAL,
2576 "unexpected character in kind specification: \'%c\'",
2578 k = vStringValue (longName);
2579 r = enableLanguageKindForName (language, k, mode);
2580 if (! r)
2581 error (WARNING, "Unsupported kind: '%s' for --%s option",
2582 k, option);
2584 inLongName = false;
2585 vStringClear (longName);
2586 break;
2587 default:
2588 if (inLongName)
2589 vStringPut (longName, c);
2590 else
2592 r = enableLanguageKindForLetter (language, c, mode);
2593 if (! r)
2594 error (WARNING, "Unsupported kind: '%c' for --%s option",
2595 c, option);
2597 break;
2602 static void freeKdef (kindDefinition *kdef)
2604 eFree (kdef->name);
2605 eFree (kdef->description);
2606 eFree (kdef);
2609 static char *extractDescriptionAndFlags(const char *input, const char **flags)
2611 vString *vdesc = vStringNew();
2612 bool escaped = false;
2614 if (flags)
2615 *flags = NULL;
2617 while (*input != '\0')
2619 if (escaped)
2621 vStringPut (vdesc, *input);
2622 escaped = false;
2625 else if (*input == '\\')
2626 escaped = true;
2627 else if (*input == LONG_FLAGS_OPEN)
2629 if (flags)
2630 *flags = input;
2631 break;
2633 else
2634 vStringPut (vdesc, *input);
2635 input++;
2637 return vStringDeleteUnwrap(vdesc);
2640 static void pre_kind_def_flag_refonly_long (const char* const optflag,
2641 const char* const param, void* data)
2643 kindDefinition *kdef = data;
2644 kdef->referenceOnly = true;
2647 static flagDefinition PreKindDefFlagDef [] = {
2648 { '\0', "_refonly", NULL, pre_kind_def_flag_refonly_long,
2649 NULL, "use this kind reference tags only"},
2652 static bool processLangDefineKind(const langType language,
2653 const char *const option,
2654 const char *const parameter)
2656 parserObject *parser;
2658 kindDefinition *kdef;
2659 char letter;
2660 const char * p = parameter;
2661 char *name;
2662 char *description;
2663 const char *name_start;
2664 const char *marker_end;
2665 size_t name_len;
2666 const char *flags;
2669 Assert (0 <= language && language < (int) LanguageCount);
2670 parser = LanguageTable + language;
2672 Assert (p);
2674 if (p[0] == '\0')
2675 error (FATAL, "no kind definition specified in \"--%s\" option", option);
2677 letter = p[0];
2678 if (letter == ',')
2679 error (FATAL, "no kind letter specified in \"--%s\" option", option);
2680 if (/* See #1697. isalnum expects 0~255 as the range of characters. */
2681 !isalpha ((unsigned char)letter)
2683 error (FATAL, "the kind letter given in \"--%s\" option is not an alphabet", option);
2684 else if (letter == KIND_FILE_DEFAULT_LETTER)
2685 error (FATAL, "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind",
2686 KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME);
2687 else if (getKindForLetter (parser->kindControlBlock, letter))
2689 error (WARNING, "the kind for letter `%c' specified in \"--%s\" option is already defined.",
2690 letter, option);
2691 return true;
2694 if (p[1] != ',')
2695 error (FATAL, "wrong kind definition in \"--%s\" option: no comma after letter", option);
2697 p += 2;
2698 if (p[0] == '\0')
2699 error (FATAL, "no kind name specified in \"--%s\" option", option);
2700 marker_end = strchr (p, ',');
2701 if (!marker_end)
2702 error (FATAL, "no kind description specified in \"--%s\" option", option);
2704 name_start = p;
2705 while (p != marker_end)
2707 if (p == name_start)
2709 if (!isalpha((unsigned char) *p))
2711 char *name_in_msg = eStrndup (name_start, marker_end - name_start);
2712 error (FATAL,
2713 "a kind name doesn't start with an alphabetical character: "
2714 "'%s' in \"--%s\" option",
2715 name_in_msg, option);
2718 else
2720 if (!isalnum ((unsigned char) *p))
2722 char *name_in_msg = eStrndup (name_start, marker_end - name_start);
2723 error (FATAL,
2724 "non-alphanumeric char is used as part of kind name: "
2725 "'%s' in \"--%s\" option",
2726 name_in_msg, option);
2729 p++;
2732 if (marker_end == name_start)
2733 error (FATAL, "the kind name in \"--%s\" option is empty", option);
2735 name_len = marker_end - name_start;
2736 if (strncmp (name_start, KIND_FILE_DEFAULT_NAME, name_len) == 0)
2737 error (FATAL,
2738 "the kind name " KIND_FILE_DEFAULT_NAME " in \"--%s\" option is reserved",
2739 option);
2741 name = eStrndup (name_start, name_len);
2742 if (getKindForName (parser->kindControlBlock, name))
2744 error (WARNING, "the kind for name `%s' specified in \"--%s\" option is already defined.",
2745 name, option);
2746 eFree (name);
2747 return true;
2750 p++;
2751 if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN)
2752 error (FATAL, "found an empty kind description in \"--%s\" option", option);
2754 description = extractDescriptionAndFlags (p, &flags);
2756 kdef = xCalloc (1, kindDefinition);
2757 kdef->enabled = true;
2758 kdef->letter = letter;
2759 kdef->name = name;
2760 kdef->description = description;
2761 if (flags)
2762 flagsEval (flags, PreKindDefFlagDef, ARRAY_SIZE (PreKindDefFlagDef), kdef);
2764 defineKind (parser->kindControlBlock, kdef, freeKdef);
2765 return true;
2768 static void freeRdef (roleDefinition *rdef)
2770 eFree (rdef->name);
2771 eFree (rdef->description);
2772 eFree (rdef);
2775 static bool processLangDefineRole(const langType language,
2776 const char *const kindSpec,
2777 const char *const option,
2778 const char *const parameter)
2780 parserObject *parser;
2782 kindDefinition *kdef;
2783 roleDefinition *rdef;
2784 char *name;
2785 char *description;
2787 Assert (0 <= language && language < (int) LanguageCount);
2788 Assert (parameter);
2790 parser = LanguageTable + language;
2792 if (*kindSpec == '{')
2794 const char *end = strchr (kindSpec, '}');
2795 if (end == NULL)
2796 error (FATAL, "no '}' representing the end of kind name in --%s option: %s",
2797 option, kindSpec);
2798 if (*(end + 1) != '\0')
2799 error (FATAL, "garbage after the kind specification %s in --%s option",
2800 kindSpec, option);
2801 char *kindName = eStrndup (kindSpec + 1, end - (kindSpec + 1));
2802 if (strcmp (kindName, KIND_FILE_DEFAULT_NAME) == 0)
2803 error (FATAL, "don't define a role for %c/%s kind; it has no role: --%s",
2804 KIND_FILE_DEFAULT_LETTER, KIND_FILE_DEFAULT_NAME,
2805 option);
2806 kdef = getKindForName (parser->kindControlBlock, kindName);
2807 if (kdef == NULL)
2808 error (FATAL, "the kind for name `%s' specified in \"--%s\" option is not defined.",
2809 kindName, option);
2810 eFree (kindName);
2812 else
2814 char kletter = *kindSpec;
2815 if (!isalnum ((unsigned char)kletter))
2816 error (FATAL, "the kind letter given in \"--%s\" option is not an alphabet or a number", option);
2817 else if (kletter == KIND_FILE_DEFAULT_LETTER)
2818 error (FATAL, "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind, and no role can be attached to it",
2819 KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME);
2820 else if (*(kindSpec + 1) != '\0')
2821 error (FATAL, "more than one letters are specified as a kind spec in \"--%s\" option: use `{' and `}' for specifying a kind name",
2822 option);
2824 kdef = getKindForLetter (parser->kindControlBlock, kletter);
2825 if (kdef == NULL)
2827 error (FATAL, "the kind for letter `%c' specified in \"--%s\" option is not defined.",
2828 *kindSpec, option);
2829 return true;
2833 const char * p = parameter;
2834 const char *tmp_end = strchr (p, ',');
2835 if (!tmp_end)
2836 error (FATAL, "no role description specified in \"--%s\" option", option);
2838 const char * tmp_start = p;
2839 while (p != tmp_end)
2841 if (!isalnum ((unsigned char) *p))
2842 error (FATAL, "unacceptable char as part of role name in \"--%s\" option: %c",
2843 option, *p);
2844 p++;
2847 if (tmp_end == tmp_start)
2848 error (FATAL, "the role name in \"--%s\" option is empty", option);
2850 name = eStrndup (tmp_start, tmp_end - tmp_start);
2851 if (getRoleForName (parser->kindControlBlock, kdef->id, name))
2853 error (WARNING, "the role for name `%s' specified in \"--%s\" option is already defined.",
2854 name, option);
2855 eFree (name);
2856 return true;
2859 p++;
2860 if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN)
2861 error (FATAL, "found an empty role description in \"--%s\" option", option);
2863 const char *flags;
2864 description = extractDescriptionAndFlags (p, &flags);
2866 rdef = xCalloc (1, roleDefinition);
2867 rdef->enabled = true;
2868 rdef->name = name;
2869 rdef->description = description;
2871 if (flags)
2872 flagsEval (flags, NULL, 0, rdef);
2874 defineRole (parser->kindControlBlock, kdef->id, rdef, freeRdef);
2876 return true;
2879 extern bool processKinddefOption (const char *const option, const char * const parameter)
2881 langType language;
2883 language = getLanguageComponentInOption (option, "kinddef-");
2884 if (language == LANG_IGNORE)
2885 return false;
2887 return processLangDefineKind (language, option, parameter);
2890 extern bool processRoledefOption (const char *const option, const char * const parameter)
2892 #define PREFIX "_roledef-"
2893 #define PREFIX_LEN strlen(PREFIX)
2895 langType language = getLanguageComponentInOption (option, PREFIX);
2896 if (language == LANG_IGNORE)
2897 return false;
2899 Assert (0 <= language && language < (int) LanguageCount);
2900 const char* kindSpec = option + PREFIX_LEN + strlen (getLanguageName (language));
2901 if (*kindSpec == '\0')
2902 error (FATAL, "no kind is specifined in \"--%s=%s\"", option, parameter);
2903 if (*kindSpec != '.')
2904 error (FATAL, "no delimiter (.) where a kindspec starts is found in \"--%s\": %c",
2905 option, *kindSpec);
2906 kindSpec++;
2908 return processLangDefineRole (language, kindSpec, option, parameter);
2909 #undef PREFIX
2910 #undef PREFIX_LEN
2913 struct langKindDefinitionStruct {
2914 const char *const option;
2915 const char *const parameter;
2917 static void processLangKindDefinitionEach(
2918 langType lang, void* user_data)
2920 struct langKindDefinitionStruct *arg = user_data;
2921 processLangKindDefinition (lang, arg->option, arg->parameter);
2924 static bool parameterEnablingAllOrFileKind (const char *const option,
2925 const char *const parameter,
2926 bool following_plus_or_minus_op)
2928 size_t file_long_flag_len = strlen(KIND_FILE_DEFAULT_NAME);
2930 switch (parameter[0])
2932 /* Though only '*' is documented as an acceptable kind spec for
2933 * --kinds-all option in our man page, we accept '\0' here because
2934 * it will be useful for testing purpose. */
2935 case '\0':
2936 if (following_plus_or_minus_op)
2937 error(FATAL, "no kind specification after + (or -) in --%s option",
2938 option);
2939 else
2940 return true;
2941 case '+':
2942 case '-':
2943 if (following_plus_or_minus_op)
2944 error(FATAL, "don't repeat + (nor -) in --%s option",
2945 option);
2946 else
2947 return parameterEnablingAllOrFileKind (option, parameter + 1, true);
2948 case KIND_WILDCARD_LETTER:
2949 if (following_plus_or_minus_op)
2950 error(FATAL, "don't use '*' after + (nor -) in --%s option",
2951 option);
2952 else
2953 return parameterEnablingAllOrFileKind (option, parameter + 1, false);
2954 case KIND_FILE_DEFAULT_LETTER:
2955 return parameterEnablingAllOrFileKind (option, parameter + 1, false);
2956 case '{':
2957 if (strncmp (parameter + 1, KIND_FILE_DEFAULT_NAME, file_long_flag_len) == 0
2958 && parameter [1 + file_long_flag_len] == '}')
2959 return parameterEnablingAllOrFileKind (option,
2960 parameter + 1 + file_long_flag_len + 1,
2961 false);
2962 break;
2964 return false;
2967 extern bool processKindsOption (
2968 const char *const option, const char *const parameter)
2970 #define PREFIX "kinds-"
2971 #define PREFIX_LEN strlen(PREFIX)
2973 bool handled = false;
2974 struct langKindDefinitionStruct arg = {
2975 .option = option,
2976 .parameter = parameter,
2978 langType language;
2980 const char* const dash = strchr (option, '-');
2981 if (dash != NULL &&
2982 (strcmp (dash + 1, "kinds") == 0 || strcmp (dash + 1, "types") == 0))
2984 size_t len = dash - option;
2985 char *langName = eStrndup (option, len);
2987 if ((len == 3) && (strcmp (langName, RSV_LANG_ALL) == 0))
2989 error (WARNING,
2990 "\"--%s\" option is obsolete; use \"--kinds-%s\" instead",
2991 option, langName);
2992 if (!parameterEnablingAllOrFileKind (option, parameter, false))
2993 error (FATAL, "only '*', 'F', \"{file}\" or their combination is acceptable as kind letter for --%s", option);
2994 foreachLanguage(processLangKindDefinitionEach, &arg);
2996 else
2998 language = getNamedLanguage (langName, 0);
2999 if (language == LANG_IGNORE)
3000 error (WARNING, "Unknown language \"%s\" in \"%s\" option", langName, option);
3001 else
3002 processLangKindDefinition (language, option, parameter);
3004 eFree (langName);
3005 handled = true;
3007 else if ( strncmp (option, PREFIX, PREFIX_LEN) == 0 )
3009 const char* lang;
3011 lang = option + PREFIX_LEN;
3012 if (lang[0] == '\0')
3013 error (WARNING, "No language given in \"%s\" option", option);
3014 else if (strcmp (lang, RSV_LANG_ALL) == 0)
3016 if (!parameterEnablingAllOrFileKind (option, parameter, false))
3017 error (FATAL, "only '*', 'F', \"{file}\" or their combination is acceptable as kind letter for --%s", option);
3018 foreachLanguage(processLangKindDefinitionEach, &arg);
3020 else
3022 language = getNamedLanguage (lang, 0);
3023 if (language == LANG_IGNORE)
3024 error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option);
3025 else
3026 processLangKindDefinition (language, option, parameter);
3028 handled = true;
3030 return handled;
3031 #undef PREFIX
3032 #undef PREFIX_LEN
3036 * The argument specification for --roles-<LANG>:<KIND>= option
3037 * =====================================================================
3039 * --roles-all.*=
3040 * --roles-all=
3041 * => Disable all roles of all kinds in all languages.
3043 * --roles-all.*='*'
3044 * --roles-all='*'
3045 * => Enable all roles of all kinds in all languages.
3047 * --roles-<LANG>.*=
3048 * --roles-<LANG>=
3049 * => Disable all roles of all kinds.
3051 * --roles-<LANG>.*=*
3052 * --roles-<LANG>=*
3053 * => Enable all roles of all kinds.
3055 * --roles-<LANG>.{kind}=
3056 * --roles-<LANG>.k=
3057 * => Disable all roles of the kind specified with a letter.
3059 * --roles-<LANG>.{kind}=*
3060 * --roles-<LANG>.k=*
3061 * => Enable all roles of the kind specified with a letter.
3063 * --roles-<LANG>.{kind}=[+|-]{role}
3064 * --roles-<LANG>.k=[+|-]{role}
3065 * => Enable/disable the role of the kind specified with a letter.
3068 * Examples of combination
3069 * ---------------------------------------------------------------------
3071 * --roles-<LANG>.k0=+{role0}-{role1}{role2}
3072 * --roles-<LANG>.{kind1}=+{role0}-{role1}{role2}
3075 * How --roledef should be change to align --roles-<LANG> notation
3076 * ---------------------------------------------------------------------
3078 * --_roledef-<LANG>.k=role,description
3079 * --_roledef-<LANG>.{kind}=role,description
3081 * The notation was
3082 * --_roledef-<LANG>=k.role,description
3085 * How --param should be change to align --roles-<LANG> notation
3086 * ---------------------------------------------------------------------
3088 * --param-<LANG>.name=argument
3090 * The notation was
3091 * --param-<LANG>:name=argument
3094 * How --paramdef should be to align --roles-<LANG> notation
3095 * ---------------------------------------------------------------------
3097 * --_paramdef-<LANG>.name=[ default (desription) ]
3100 * Discussion: which shoule we use . or : ?
3101 * ---------------------------------------------------------------------
3103 * `.' is better because `:' implies fields.
3106 struct langKindRoleDefinitionStruct {
3107 int kindIndex;
3108 const char *const option;
3109 const char *const parameter;
3112 typedef void (*kindCallback) (langType language, int kindIndex, void* user_data);
3113 static void foreachKind(langType language, kindCallback callback, void *user_data)
3115 unsigned int c = countLanguageKinds (language);
3116 for (unsigned int i = 0; i < c; i++)
3117 callback (language, (int)i, user_data);
3120 static void resetKindRoles (const langType language, int kindIndex, const bool mode)
3122 Assert (0 <= language && language < (int) LanguageCount);
3123 unsigned int c = countLanguageRoles (language, kindIndex);
3125 for (unsigned int i = 0; i < c; i++)
3127 roleDefinition* rdef = getLanguageRole (language, kindIndex, (int)i);
3128 enableRole (rdef, mode);
3132 static void resetKindRolesAsCallback (const langType language, int kindIndex, void *user_data)
3134 bool mode = (bool)user_data;
3135 resetKindRoles (language, kindIndex, mode);
3138 static void processLangKindRoleDefinition (
3139 const langType language, const int kindIndex, const char *const option,
3140 const char *const parameter)
3142 Assert (0 <= language && language < (int) LanguageCount);
3143 Assert (kindIndex != KIND_GHOST_INDEX);
3144 initializeParser (language);
3146 const char *p = parameter;
3147 bool mode = true;
3149 if (*p == '\0')
3151 resetKindRoles (language, kindIndex, false);
3152 return;
3154 else if (*p != '+' && *p != '-' )
3155 resetKindRoles (language, kindIndex, false);
3157 while (*p != '\0')
3159 if (*p == '+')
3161 mode = true;
3162 p++;
3164 else if (*p == '-')
3166 mode = false;
3167 p++;
3169 else if (*p == '{')
3171 p++;
3172 char *q = strchr (p, '}');
3173 if (!q)
3175 error (FATAL, "no '}' representing the end of role name in --%s option: %s",
3176 option, p);
3177 if (p == q)
3178 error (FATAL, "empty role for the kind letter: %c",
3179 getLanguageKind (language, kindIndex)->letter);
3181 char *rname = eStrndup (p, q - p);
3182 roleDefinition *rdef = getLanguageRoleForName (language, kindIndex, rname);
3183 if (!rdef)
3184 error (WARNING, "no such role: \"%s\" in kind \'%c\' in language \"%s\"",
3185 rname, getLanguageKind (language, kindIndex)->letter,
3186 getLanguageName (language));
3187 else
3188 enableRole (rdef, mode);
3189 eFree (rname);
3190 p = q + 1;
3192 else if (*p == '*')
3194 resetKindRoles (language, kindIndex, true);
3195 p++;
3197 else
3198 error (FATAL, "unexpected character %c in --%s=%s option",
3199 *p, option, parameter);
3203 static void processLangKindRoleDefinitionEach (langType language, void* user_data)
3205 struct langKindRoleDefinitionStruct *arg = user_data;
3207 if (arg->kindIndex == KIND_GHOST_INDEX)
3209 initializeParser (language);
3210 foreachKind (language, resetKindRolesAsCallback,
3211 ((*(arg->parameter) == '*')? (void *)true: (void *)false));
3213 else
3214 processLangKindRoleDefinition (language, arg->kindIndex,
3215 arg->option, arg->parameter);
3218 extern bool processRolesOption (const char *const option, const char *const parameter)
3220 #define PREFIX "roles-"
3221 #define PREFIX_LEN strlen(PREFIX)
3223 if ( strncmp (option, PREFIX, PREFIX_LEN) != 0 )
3224 return false;
3226 const char* lang = option + PREFIX_LEN;
3227 if (lang[0] == '\0')
3229 error (WARNING, "no language given in \"%s\" option", option);
3230 return true;
3234 * --roles-all.*=
3235 * --roles-all=
3236 * => Disable all roles of all kinds in all languages.
3238 * --roles-all.*='*'
3239 * --roles-all='*'
3240 * => Enable all roles of all kinds in all languages.
3242 if (strncmp (lang, RSV_LANG_ALL, strlen(RSV_LANG_ALL)) == 0)
3244 if (lang [strlen (RSV_LANG_ALL)] == '\0'
3245 || (strcmp (lang + strlen (RSV_LANG_ALL), ".*") == 0))
3247 if (*parameter == '\0'
3248 || strcmp(parameter, "*") == 0)
3250 struct langKindRoleDefinitionStruct arg = {
3251 .kindIndex = KIND_GHOST_INDEX,
3252 .option = option,
3253 .parameter = parameter,
3255 foreachLanguage (processLangKindRoleDefinitionEach, &arg);
3256 return true;
3258 else
3259 error (FATAL, "only '*' or '' (empty string) is acceptable as an argument for --%s: %s",
3260 option,
3261 parameter);
3263 else if (lang [strlen(RSV_LANG_ALL)] == '.')
3264 error (FATAL, "only '*' or '' (empty string) is acceptable as a kind spec for --%sall: --%s",
3265 PREFIX,
3266 option);
3269 /* Decide the language. */
3270 langType language;
3271 const char *dot = strchr (lang, '.');
3272 if (dot)
3273 language = getNamedLanguage (lang, dot - lang);
3274 else
3275 language = getNamedLanguage (lang, 0);
3277 if (language == LANG_IGNORE)
3279 char *lang0 = dot? eStrndup (lang, dot - lang): NULL;
3280 error (WARNING, "unknown language \"%s\" in --%s option",
3281 (lang0? lang0: lang), option);
3282 if (lang0)
3283 eFree (lang0);
3284 return true;
3288 * --roles-<LANG>.*=
3289 * --roles-<LANG>=
3290 * => Disable all roles of all kinds.
3292 * --roles-<LANG>.*=*
3293 * --roles-<LANG>=*
3294 * => Enable all roles of all kinds.
3296 if (dot == NULL || (strcmp (dot, ".*") == 0))
3298 if (*parameter == '\0'
3299 || strcmp(parameter, "*") == 0)
3301 foreachKind (language, resetKindRolesAsCallback,
3302 ((*parameter == '*')? (void*)true: (void*)false));
3303 return true;
3305 else
3306 error (FATAL, "only '*' or '' (empty string) is acceptable as an argument for --%s: %s",
3307 option,
3308 parameter);
3311 /* Decide the kind of the language. */
3312 parserObject *parser = LanguageTable + language;
3313 int kindIndex = KIND_GHOST_INDEX;
3314 const char *kind = dot + 1;
3315 if (*kind == '{')
3317 const char *name_end = strchr (kind, '}');
3318 if (name_end == NULL)
3319 error (FATAL, "no '}' representing the end of kind name in --%s option: %s",
3320 option, kind);
3321 char *kindName = eStrndup (kind + 1, name_end - (kind + 1));
3322 if (strcmp (kindName, KIND_FILE_DEFAULT_NAME) == 0)
3324 error (WARNING, "don't enable/disable a role in %c/%s kind; it has no role: --%s",
3325 KIND_FILE_DEFAULT_LETTER, KIND_FILE_DEFAULT_NAME,
3326 option);
3327 return true;
3329 kindIndex = getKindIndexForName (parser->kindControlBlock, kindName);
3330 if (kindIndex == KIND_GHOST_INDEX)
3332 eFree (kindName);
3333 error (WARNING, "no such kind name as specified in --%s option", option);
3334 return true;
3336 if (*(name_end + 1) != '\0')
3337 error (FATAL, "garbage after the kind specification {%s} in --%s option",
3338 kindName, option);
3339 eFree (kindName);
3341 else if (isalpha ((unsigned char)*kind))
3343 if (*kind == KIND_FILE_DEFAULT_LETTER)
3345 error (WARNING, "don't enable/disable a role in %c/%s kind; it has no role: --%s",
3346 KIND_FILE_DEFAULT_LETTER, KIND_FILE_DEFAULT_NAME,
3347 option);
3348 return true;
3350 kindIndex = getKindIndexForLetter (parser->kindControlBlock, *kind);
3351 if (kindIndex == KIND_GHOST_INDEX)
3353 error (WARNING, "no such kind letter as specified in --%s option", option);
3354 return true;
3356 if (*(kind + 1) != '\0')
3357 error (FATAL, "garbage after the kind specification '%c' in --%s option",
3358 *kind, option);
3360 else
3361 error (FATAL, "'%c', unexpected character in --%s", *kind, option);
3365 * --roles-<LANG>.{kind}=
3366 * --roles-<LANG>.k=
3367 * => Disable all roles of the kind specified with a letter.
3369 * --roles-<LANG>.{kind}=*
3370 * --roles-<LANG>.k=*
3371 * => Enable all roles of the kind specified with a letter.
3373 * --roles-<LANG>.{kind}=[+|-|]{role}
3374 * --roles-<LANG>.k=[+|-|]{role}
3375 * => Enable/disable the role of the kind specified with a letter.
3377 processLangKindRoleDefinition (language, kindIndex, option, parameter);
3379 return true;
3380 #undef PREFIX
3381 #undef PREFIX_LEN
3384 extern void printLanguageRoles (const langType language, const char* kindspecs,
3385 bool withListHeader, bool machinable, FILE *fp)
3387 struct colprintTable *table = roleColprintTableNew();
3388 parserObject *parser;
3390 initializeParser (language);
3392 if (language == LANG_AUTO)
3394 for (unsigned int i = 0 ; i < LanguageCount ; ++i)
3396 if (!isLanguageVisible (i))
3397 continue;
3399 parser = LanguageTable + i;
3400 roleColprintAddRoles (table, parser->kindControlBlock, kindspecs);
3403 else
3405 parser = LanguageTable + language;
3406 roleColprintAddRoles (table, parser->kindControlBlock, kindspecs);
3409 roleColprintTablePrint (table, (language != LANG_AUTO),
3410 withListHeader, machinable, fp);
3411 colprintTableDelete (table);
3414 static void printKinds (langType language, bool indent,
3415 struct colprintTable * table)
3417 const parserObject *parser;
3418 struct kindControlBlock *kcb;
3419 Assert (0 <= language && language < (int) LanguageCount);
3421 initializeParser (language);
3422 parser = LanguageTable + language;
3423 kcb = parser->kindControlBlock;
3425 if (table)
3426 kindColprintAddLanguageLines (table, kcb);
3427 else
3429 for (unsigned int i = 0 ; i < countKinds(kcb) ; ++i)
3430 printKind (getKind(kcb, i), indent);
3434 extern void printLanguageKinds (const langType language, bool allKindFields,
3435 bool withListHeader, bool machinable, FILE *fp)
3437 struct colprintTable * table = NULL;
3439 if (allKindFields)
3440 table = kindColprintTableNew ();
3442 if (language == LANG_AUTO)
3444 for (unsigned int i = 0 ; i < LanguageCount ; ++i)
3446 const parserDefinition* const lang = LanguageTable [i].def;
3448 if (lang->invisible)
3449 continue;
3451 if (!table)
3452 printf ("%s%s\n", lang->name, isLanguageEnabled (i) ? "" : " [disabled]");
3453 printKinds (i, true, table);
3456 else
3457 printKinds (language, false, table);
3459 if (allKindFields)
3461 kindColprintTablePrint(table, (language == LANG_AUTO)? 0: 1,
3462 withListHeader, machinable, fp);
3463 colprintTableDelete (table);
3467 extern bool processParamOption (
3468 const char *const option, const char *const value)
3470 langType language;
3471 const char* name;
3472 const char* sep;
3474 language = getLanguageComponentInOption (option, "param-");
3475 if (language == LANG_IGNORE)
3476 return false;
3478 sep = option + strlen ("param-") + strlen (getLanguageName (language));
3479 /* `:' is only for keeping self compatibility */
3480 if (! (*sep == '.' || *sep == ':' ))
3481 error (FATAL, "no separator(.) is given for %s=%s", option, value);
3482 name = sep + 1;
3484 if (value == NULL || value [0] == '\0')
3485 error (FATAL, "no value is given for %s", option);
3487 if (applyLanguageParam (language, name, value))
3488 propagateParamToOptscript (LanguageTable [language].lregexControlBlock,
3489 name, value);
3490 return true;
3493 static void freePdef (paramDefinition *pdef)
3495 eFree ((void *)pdef->name);
3496 eFree ((void *)pdef->desc);
3497 eFree (pdef);
3500 static bool processLangDefineParam (const langType language,
3501 const char *const option,
3502 const char *const parameter)
3504 parserObject *parser;
3506 paramDefinition *pdef;
3507 const char * p = parameter;
3508 const char *name_end;
3509 const char *desc;
3510 const char *flags;
3512 Assert (0 <= language && language < (int) LanguageCount);
3513 Assert (p);
3515 if (p[0] == '\0')
3516 error (FATAL, "no parameter definition specified in \"--%s\" option", option);
3518 name_end = strchr (p, ',');
3519 if (!name_end)
3520 error (FATAL, "no parameter description specified in \"--%s\" option", option);
3521 else if (name_end == p)
3522 error (FATAL, "the parameter name in \"--%s\" option is empty", option);
3524 for (; p < name_end; p++)
3526 if (!isalnum ((unsigned char) *p) && *p != '_')
3527 error (FATAL, "unacceptable char as part of extra name in \"--%s\" option",
3528 option);
3531 p++;
3532 if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN)
3533 error (FATAL, "parameter description in \"--%s\" option is empty", option);
3535 desc = extractDescriptionAndFlags (p, &flags);
3537 pdef = xCalloc (1, paramDefinition);
3538 pdef->name = eStrndup (parameter, name_end - parameter);
3539 pdef->desc = desc;
3541 #if 0
3542 if (flags)
3543 flagsEval (flags, NULL, 0, pdef);
3544 #endif
3546 parser = LanguageTable + language;
3547 defineParam (parser->paramControlBlock, pdef, freePdef);
3548 return true;
3551 extern bool processParamdefOption (const char *const option, const char *const value)
3553 langType language;
3555 language = getLanguageComponentInOption (option, "_paramdef-");
3556 if (language == LANG_IGNORE)
3557 return false;
3559 return processLangDefineParam (language, option, value);
3562 static void printParams (struct colprintTable *table, langType language)
3564 Assert (0 <= language && language < (int) LanguageCount);
3566 initializeParser (language);
3567 paramColprintAddParams (table,
3568 LanguageTable [language].paramControlBlock);
3571 extern void printLanguageParams (const langType language,
3572 bool withListHeader, bool machinable, FILE *fp)
3574 struct colprintTable *table = paramColprintTableNew();
3576 if (language == LANG_AUTO)
3578 for (unsigned int i = 0; i < LanguageCount ; ++i)
3580 const parserDefinition* const lang = LanguageTable [i].def;
3582 if (lang->invisible)
3583 continue;
3585 printParams (table, i);
3588 else
3589 printParams (table, language);
3591 paramColprintTablePrint (table, (language != LANG_AUTO),
3592 withListHeader, machinable, fp);
3593 colprintTableDelete (table);
3596 static void processLangAliasOption (const langType language,
3597 const char *const parameter)
3599 const char* alias;
3600 const parserObject * parser;
3602 Assert (0 <= language && language < (int) LanguageCount);
3603 parser = LanguageTable + language;
3605 if (parameter[0] == '\0')
3607 clearLanguageAliases (language);
3608 verbose ("clear aliases for %s\n", parser->def->name);
3610 else if (strcmp (parameter, RSV_LANGMAP_DEFAULT) == 0)
3612 installLanguageAliasesDefault (language);
3613 verbose ("reset aliases for %s\n", parser->def->name);
3615 else if (parameter[0] == '+')
3617 alias = parameter + 1;
3618 addLanguageAlias(language, alias);
3619 verbose ("add an alias %s to %s\n", alias, parser->def->name);
3621 else if (parameter[0] == '-')
3623 if (parser->currentAliases)
3625 alias = parameter + 1;
3626 if (stringListDeleteItemExtension (parser->currentAliases, alias))
3628 verbose ("remove an alias %s from %s\n", alias, parser->def->name);
3632 else
3634 alias = parameter;
3635 clearLanguageAliases (language);
3636 addLanguageAlias(language, alias);
3637 verbose ("set alias %s to %s\n", alias, parser->def->name);
3642 extern bool processAliasOption (
3643 const char *const option, const char *const parameter)
3645 langType language;
3647 Assert (parameter);
3649 #define PREFIX "alias-"
3650 if (strcmp (option, "alias-" RSV_LANG_ALL) == 0)
3652 if ((parameter[0] == '\0')
3653 || (strcmp (parameter, RSV_LANGMAP_DEFAULT) == 0))
3655 for (unsigned int i = 0; i < LanguageCount; i++)
3657 clearLanguageAliases (i);
3658 verbose ("clear aliases for %s\n", getLanguageName(i));
3661 if (parameter[0] != '\0')
3663 verbose (" Installing default language aliases:\n");
3664 installLanguageAliasesDefaults ();
3667 else
3669 error (WARNING, "Use \"%s\" option for reset (\"default\") or clearing (\"\")", option);
3670 return false;
3672 return true;
3675 language = getLanguageComponentInOption (option, "alias-");
3676 if (language == LANG_IGNORE)
3677 return false;
3678 #undef PREFIX
3680 processLangAliasOption (language, parameter);
3681 return true;
3684 static void printMaps (const langType language, langmapType type)
3686 const parserObject* parser;
3687 unsigned int i;
3689 parser = LanguageTable + language;
3690 printf ("%-8s", parser->def->name);
3691 if (parser->currentPatterns != NULL && (type & LMAP_PATTERN))
3692 for (i = 0 ; i < stringListCount (parser->currentPatterns) ; ++i)
3693 printf (" %s", vStringValue (
3694 stringListItem (parser->currentPatterns, i)));
3695 if (parser->currentExtensions != NULL && (type & LMAP_EXTENSION))
3696 for (i = 0 ; i < stringListCount (parser->currentExtensions) ; ++i)
3697 printf (" *.%s", vStringValue (
3698 stringListItem (parser->currentExtensions, i)));
3699 putchar ('\n');
3702 static struct colprintTable *mapColprintTableNew (langmapType type)
3704 if ((type & LMAP_ALL) == LMAP_ALL)
3705 return colprintTableNew ("L:LANGUAGE", "L:TYPE", "L:MAP", NULL);
3706 else if (type & LMAP_PATTERN)
3707 return colprintTableNew ("L:LANGUAGE", "L:PATTERN", NULL);
3708 else if (type & LMAP_EXTENSION)
3709 return colprintTableNew ("L:LANGUAGE", "L:EXTENSION", NULL);
3710 else
3712 AssertNotReached ();
3713 return NULL;
3717 static void mapColprintAddLanguage (struct colprintTable * table,
3718 langmapType type,
3719 const parserObject* parser)
3721 struct colprintLine * line;
3722 unsigned int count;
3723 unsigned int i;
3725 if ((type & LMAP_PATTERN) && (0 < (count = stringListCount (parser->currentPatterns))))
3727 for (i = 0; i < count; i++)
3729 line = colprintTableGetNewLine (table);
3730 vString *pattern = stringListItem (parser->currentPatterns, i);
3732 colprintLineAppendColumnCString (line, parser->def->name);
3733 if (type & LMAP_EXTENSION)
3734 colprintLineAppendColumnCString (line, "pattern");
3735 colprintLineAppendColumnVString (line, pattern);
3739 if ((type & LMAP_EXTENSION) && (0 < (count = stringListCount (parser->currentExtensions))))
3741 for (i = 0; i < count; i++)
3743 line = colprintTableGetNewLine (table);
3744 vString *extension = stringListItem (parser->currentExtensions, i);
3746 colprintLineAppendColumnCString (line, parser->def->name);
3747 if (type & LMAP_PATTERN)
3748 colprintLineAppendColumnCString (line, "extension");
3749 colprintLineAppendColumnVString (line, extension);
3754 extern void printLanguageMaps (const langType language, langmapType type,
3755 bool withListHeader, bool machinable, FILE *fp)
3757 /* DON'T SORT THE LIST
3759 The order of listing should be equal to the order of matching
3760 for the parser selection. */
3762 struct colprintTable * table = NULL;
3763 if (type & LMAP_TABLE_OUTPUT)
3764 table = mapColprintTableNew(type);
3766 if (language == LANG_AUTO)
3768 for (unsigned int i = 0 ; i < LanguageCount ; ++i)
3770 if (!isLanguageVisible (i))
3771 continue;
3773 if (type & LMAP_TABLE_OUTPUT)
3775 const parserObject* parser = LanguageTable + i;
3777 mapColprintAddLanguage (table, type, parser);
3779 else
3780 printMaps (i, type);
3783 else
3785 Assert (0 <= language && language < (int) LanguageCount);
3787 if (type & LMAP_TABLE_OUTPUT)
3789 const parserObject* parser = LanguageTable + language;
3791 mapColprintAddLanguage (table, type, parser);
3793 else
3794 printMaps (language, type);
3798 if (type & LMAP_TABLE_OUTPUT)
3800 colprintTablePrint (table, (language == LANG_AUTO)? 0: 1,
3801 withListHeader, machinable, fp);
3802 colprintTableDelete (table);
3806 static struct colprintTable *aliasColprintTableNew (void)
3808 return colprintTableNew ("L:LANGUAGE", "L:ALIAS", NULL);
3811 static void aliasColprintAddLanguage (struct colprintTable * table,
3812 const parserObject* parser)
3814 unsigned int count;
3816 if (parser->currentAliases && (0 < (count = stringListCount (parser->currentAliases))))
3818 for (unsigned int i = 0; i < count; i++)
3820 struct colprintLine * line = colprintTableGetNewLine (table);
3821 vString *alias = stringListItem (parser->currentAliases, i);
3823 colprintLineAppendColumnCString (line, parser->def->name);
3824 colprintLineAppendColumnVString (line, alias);
3829 extern void printLanguageAliases (const langType language,
3830 bool withListHeader, bool machinable, FILE *fp)
3832 /* DON'T SORT THE LIST
3834 The order of listing should be equal to the order of matching
3835 for the parser selection. */
3837 struct colprintTable * table = aliasColprintTableNew();
3838 const parserObject* parser;
3840 if (language == LANG_AUTO)
3842 for (unsigned int i = 0; i < LanguageCount; ++i)
3844 parser = LanguageTable + i;
3845 if (parser->def->invisible)
3846 continue;
3848 aliasColprintAddLanguage (table, parser);
3851 else
3853 Assert (0 <= language && language < (int) LanguageCount);
3854 parser = LanguageTable + language;
3855 aliasColprintAddLanguage (table, parser);
3858 colprintTablePrint (table, (language == LANG_AUTO)? 0: 1,
3859 withListHeader, machinable, fp);
3860 colprintTableDelete (table);
3863 static void printLanguage (const langType language, parserDefinition** ltable)
3865 const parserDefinition* lang;
3866 Assert (0 <= language && language < (int) LanguageCount);
3867 lang = ltable [language];
3869 if (lang->invisible)
3870 return;
3872 printf ("%s%s\n", lang->name, isLanguageEnabled (lang->id) ? "" : " [disabled]");
3875 extern void printLanguageList (void)
3877 unsigned int i;
3878 parserDefinition **ltable;
3880 ltable = xMalloc (LanguageCount, parserDefinition*);
3881 for (i = 0 ; i < LanguageCount ; ++i)
3882 ltable[i] = LanguageTable[i].def;
3883 qsort (ltable, LanguageCount, sizeof (parserDefinition*), compareParsersByName);
3885 for (i = 0 ; i < LanguageCount ; ++i)
3886 printLanguage (i, ltable);
3888 eFree (ltable);
3891 static void xtagDefinitionDestroy (xtagDefinition *xdef)
3893 eFree ((void *)xdef->name);
3894 eFree ((void *)xdef->description);
3895 eFree (xdef);
3898 static bool processLangDefineExtra (const langType language,
3899 const char *const option,
3900 const char *const parameter)
3902 xtagDefinition *xdef;
3903 const char * p = parameter;
3904 const char *name_end;
3905 const char *desc;
3906 const char *flags;
3908 Assert (0 <= language && language < (int) LanguageCount);
3909 Assert (p);
3911 if (p[0] == '\0')
3912 error (FATAL, "no extra definition specified in \"--%s\" option", option);
3914 name_end = strchr (p, ',');
3915 if (!name_end)
3916 error (FATAL, "no extra description specified in \"--%s\" option", option);
3917 else if (name_end == p)
3918 error (FATAL, "the extra name in \"--%s\" option is empty", option);
3920 for (; p < name_end; p++)
3922 if (!isalnum ((unsigned char) *p))
3923 error (FATAL, "unacceptable char as part of extra name in \"--%s\" option",
3924 option);
3927 p++;
3928 if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN)
3929 error (FATAL, "extra description in \"--%s\" option is empty", option);
3931 desc = extractDescriptionAndFlags (p, &flags);
3933 xdef = xCalloc (1, xtagDefinition);
3934 xdef->enabled = false;
3935 xdef->letter = NUL_XTAG_LETTER;
3936 xdef->name = eStrndup (parameter, name_end - parameter);
3937 xdef->description = desc;
3938 xdef->isEnabled = NULL;
3939 DEFAULT_TRASH_BOX(xdef, xtagDefinitionDestroy);
3941 if (flags)
3942 flagsEval (flags, NULL, 0, xdef);
3944 defineXtag (xdef, language);
3946 return true;
3949 extern bool processExtradefOption (const char *const option, const char *const parameter)
3951 langType language;
3953 language = getLanguageComponentInOption (option, "_" "extradef-");
3954 if (language == LANG_IGNORE)
3955 return false;
3957 return processLangDefineExtra (language, option, parameter);
3960 static void fieldDefinitionDestroy (fieldDefinition *fdef)
3962 eFree ((void *)fdef->description);
3963 eFree ((void *)fdef->name);
3964 eFree (fdef);
3967 static bool processLangDefineField (const langType language,
3968 const char *const option,
3969 const char *const parameter)
3971 fieldDefinition *fdef;
3972 const char * p = parameter;
3973 const char *name_end;
3974 const char *desc;
3975 const char *flags;
3977 Assert (0 <= language && language < (int) LanguageCount);
3978 Assert (p);
3980 if (p[0] == '\0')
3981 error (FATAL, "no field definition specified in \"--%s\" option", option);
3983 name_end = strchr (p, ',');
3984 if (!name_end)
3985 error (FATAL, "no field description specified in \"--%s\" option", option);
3986 else if (name_end == p)
3987 error (FATAL, "the field name in \"--%s\" option is empty", option);
3989 for (; p < name_end; p++)
3991 if (!isalpha ((unsigned char) *p))
3992 error (FATAL, "unacceptable char as part of field name in \"--%s\" option",
3993 option);
3996 p++;
3997 if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN)
3998 error (FATAL, "field description in \"--%s\" option is empty", option);
4000 desc = extractDescriptionAndFlags (p, &flags);
4002 fdef = xCalloc (1, fieldDefinition);
4003 fdef->enabled = false;
4004 fdef->letter = NUL_FIELD_LETTER;
4005 fdef->name = eStrndup(parameter, name_end - parameter);
4006 fdef->description = desc;
4007 fdef->isValueAvailable = NULL;
4008 fdef->getValueObject = NULL;
4009 fdef->getterValueType = NULL;
4010 fdef->setValueObject = NULL;
4011 fdef->setterValueType = NULL;
4012 fdef->checkValueForSetter = NULL;
4013 fdef->dataType = FIELDTYPE_STRING; /* TODO */
4014 fdef->ftype = FIELD_UNKNOWN;
4015 DEFAULT_TRASH_BOX(fdef, fieldDefinitionDestroy);
4017 if (flags)
4018 flagsEval (flags, NULL, 0, fdef);
4020 defineField (fdef, language);
4022 return true;
4025 extern bool processFielddefOption (const char *const option, const char *const parameter)
4027 langType language;
4029 language = getLanguageComponentInOption (option, "_fielddef-");
4030 if (language == LANG_IGNORE)
4031 return false;
4033 return processLangDefineField (language, option, parameter);
4037 * File parsing
4040 static rescanReason createTagsForFile (const langType language,
4041 const unsigned int passCount)
4043 parserDefinition *const lang = LanguageTable [language].def;
4044 rescanReason rescan = RESCAN_NONE;
4046 resetInputFile (language, passCount > 1);
4048 Assert (lang->parser || lang->parser2);
4050 notifyInputStart ();
4052 if (lang->parser != NULL)
4053 lang->parser ();
4054 else if (lang->parser2 != NULL)
4055 rescan = lang->parser2 (passCount);
4057 notifyInputEnd ();
4059 return rescan;
4062 extern void notifyLanguageRegexInputStart (langType language)
4064 parserObject *pobj = LanguageTable + language;
4066 notifyRegexInputStart(pobj->lregexControlBlock);
4069 extern void notifyLanguageRegexInputEnd (langType language)
4071 parserObject *pobj = LanguageTable + language;
4073 notifyRegexInputEnd(pobj->lregexControlBlock);
4076 static unsigned int parserCorkFlags (parserDefinition *parser)
4078 subparser *tmp;
4079 unsigned int r = 0;
4081 r |= parser->useCork;
4083 if (doesLanguageExpectCorkInRegex (parser->id)
4084 || parser->requestAutomaticFQTag)
4085 r |= CORK_QUEUE;
4087 pushLanguage (parser->id);
4088 foreachSubparser(tmp, true)
4090 langType t = getSubparserLanguage (tmp);
4091 r |= parserCorkFlags (LanguageTable[t].def);
4093 popLanguage ();
4094 return r;
4097 static void setupLanguageSubparsersInUse (const langType language)
4099 subparser *tmp;
4101 setupSubparsersInUse ((LanguageTable + language)->slaveControlBlock);
4102 foreachSubparser(tmp, true)
4104 langType t = getSubparserLanguage (tmp);
4105 enterSubparser (tmp);
4106 setupLanguageSubparsersInUse(t);
4107 leaveSubparser ();
4111 static subparser* teardownLanguageSubparsersInUse (const langType language)
4113 subparser *tmp;
4115 foreachSubparser(tmp, true)
4117 langType t = getSubparserLanguage (tmp);
4118 enterSubparser (tmp);
4119 teardownLanguageSubparsersInUse(t);
4120 leaveSubparser ();
4122 return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock);
4125 static void initializeParserStats (parserObject *parser)
4127 if (Option.printTotals > 1 && parser->used == 0 && parser->def->initStats)
4128 parser->def->initStats (parser->def->id);
4129 parser->used = 1;
4132 extern void printParserStatisticsIfUsed (langType language)
4134 parserObject *parser = &(LanguageTable [language]);
4136 if (parser->used)
4138 if (parser->def->printStats)
4140 fprintf(stderr, "\nSTATISTICS of %s\n", getLanguageName (language));
4141 fputs("==============================================\n", stderr);
4142 parser->def->printStats (language);
4144 printLanguageMultitableStatistics (language);
4148 static bool createTagsWithFallback1 (const langType language,
4149 langType *exclusive_subparser)
4151 bool tagFileResized = false;
4152 unsigned long numTags;
4153 MIOPos tagfpos;
4154 int lastPromise = getLastPromise ();
4155 unsigned int passCount = 0;
4156 rescanReason whyRescan;
4157 parserObject *parser;
4158 unsigned int corkFlags;
4159 bool useCork = false;
4161 initializeParser (language);
4162 parser = &(LanguageTable [language]);
4164 setupLanguageSubparsersInUse (language);
4166 corkFlags = parserCorkFlags (parser->def);
4167 useCork = corkFlags & CORK_QUEUE;
4168 if (useCork)
4169 corkTagFile(corkFlags);
4171 if (isXtagEnabled (XTAG_PSEUDO_TAGS))
4172 addParserPseudoTags (language);
4173 initializeParserStats (parser);
4174 numTags = numTagsAdded ();
4175 tagFilePosition (&tagfpos);
4177 anonResetMaybe (parser);
4178 parser->justRunForSchedulingBase = 0;
4180 while ( ( whyRescan =
4181 createTagsForFile (language, ++passCount) )
4182 != RESCAN_NONE)
4184 if (useCork)
4186 uncorkTagFile();
4187 corkTagFile(corkFlags);
4191 if (whyRescan == RESCAN_FAILED)
4193 /* Restore prior state of tag file.
4195 setTagFilePosition (&tagfpos, true);
4196 setNumTagsAdded (numTags);
4197 writerRescanFailed (numTags);
4198 tagFileResized = true;
4199 breakPromisesAfter(lastPromise);
4201 else if (whyRescan == RESCAN_APPEND)
4203 tagFilePosition (&tagfpos);
4204 numTags = numTagsAdded ();
4205 lastPromise = getLastPromise ();
4209 if (!parser->justRunForSchedulingBase
4210 /* Force applying regex patterns */
4211 && hasLanguageAnyRegexPatterns (language))
4212 while (readLineFromInputFile () != NULL)
4213 ; /* Do nothing */
4215 if (useCork)
4216 uncorkTagFile();
4219 subparser *s = teardownLanguageSubparsersInUse (language);
4220 if (exclusive_subparser && s)
4221 *exclusive_subparser = getSubparserLanguage (s);
4224 return tagFileResized;
4227 extern bool runParserInNarrowedInputStream (const langType language,
4228 unsigned long startLine, long startCharOffset,
4229 unsigned long endLine, long endCharOffset,
4230 unsigned long sourceLineOffset,
4231 int promise)
4233 bool tagFileResized;
4235 verbose ("runParserInNarrowedInputStream: %s; "
4236 "file: %s, "
4237 "start(line: %lu, offset: %ld, srcline: %lu)"
4238 " - "
4239 "end(line: %lu, offset: %ld)\n",
4240 getLanguageName (language),
4241 getInputFileName (),
4242 startLine, startCharOffset, sourceLineOffset,
4243 endLine, endCharOffset);
4245 pushNarrowedInputStream (
4246 doesParserRequireMemoryStream (language),
4247 startLine, startCharOffset,
4248 endLine, endCharOffset,
4249 sourceLineOffset,
4250 promise);
4251 tagFileResized = createTagsWithFallback1 (language, NULL);
4252 popNarrowedInputStream ();
4253 return tagFileResized;
4257 static bool createTagsWithFallback (
4258 const char *const fileName, const langType language,
4259 MIO *mio, time_t mtime, bool *failureInOpenning)
4261 langType exclusive_subparser = LANG_IGNORE;
4262 bool tagFileResized = false;
4264 Assert (0 <= language && language < (int) LanguageCount);
4266 if (!openInputFile (fileName, language, mio, mtime))
4268 *failureInOpenning = true;
4269 return false;
4271 *failureInOpenning = false;
4273 tagFileResized = createTagsWithFallback1 (language,
4274 &exclusive_subparser);
4275 tagFileResized = forcePromises()? true: tagFileResized;
4277 pushLanguage ((exclusive_subparser == LANG_IGNORE)
4278 ? language
4279 : exclusive_subparser);
4280 makeFileTag (fileName);
4281 popLanguage ();
4282 closeInputFile ();
4284 return tagFileResized;
4287 static void printGuessedParser (const char* const fileName, langType language)
4289 const char *parserName;
4291 if (language == LANG_IGNORE)
4293 Option.printLanguage = ((int)true) + 1;
4294 parserName = RSV_NONE;
4296 else
4298 parserName = getLanguageName (language);
4301 printf("%s: %s\n", fileName, parserName);
4304 #ifdef HAVE_ICONV
4305 static char **EncodingMap;
4306 static unsigned int EncodingMapMax;
4308 static void addLanguageEncoding (const langType language,
4309 const char *const encoding)
4311 if (language > EncodingMapMax || EncodingMapMax == 0)
4313 int i;
4314 int istart = (EncodingMapMax == 0)? 0: EncodingMapMax + 1;
4315 EncodingMap = xRealloc (EncodingMap, (language + 1), char*);
4316 for (i = istart; i <= language ; ++i)
4318 EncodingMap [i] = NULL;
4320 EncodingMapMax = language;
4322 if (EncodingMap [language])
4323 eFree (EncodingMap [language]);
4324 EncodingMap [language] = eStrdup(encoding);
4325 if (!Option.outputEncoding)
4326 Option.outputEncoding = eStrdup("UTF-8");
4329 extern bool processLanguageEncodingOption (const char *const option, const char *const parameter)
4331 langType language;
4333 language = getLanguageComponentInOption (option, "input-encoding-");
4334 if (language == LANG_IGNORE)
4335 return false;
4337 addLanguageEncoding (language, parameter);
4338 return true;
4341 extern void freeEncodingResources (void)
4343 if (EncodingMap)
4345 unsigned int i;
4346 for (i = 0 ; i <= EncodingMapMax ; ++i)
4348 if (EncodingMap [i])
4349 eFree (EncodingMap [i]);
4351 eFree (EncodingMap);
4353 if (Option.inputEncoding)
4354 eFree (Option.inputEncoding);
4355 if (Option.outputEncoding)
4356 eFree (Option.outputEncoding);
4359 extern const char *getLanguageEncoding (const langType language)
4361 if (EncodingMap && language <= EncodingMapMax && EncodingMap [language])
4362 return EncodingMap[language];
4363 else
4364 return Option.inputEncoding;
4366 #endif
4368 static void addParserPseudoTags (langType language)
4370 parserObject *parser = LanguageTable + language;
4371 if (!parser->pseudoTagPrinted)
4373 for (int i = 0; i < PTAG_COUNT; i++)
4375 if (isPtagParserSpecific (i))
4376 makePtagIfEnabled (i, language, parser);
4378 parser->pseudoTagPrinted = 1;
4382 extern bool doesParserRequireMemoryStream (const langType language)
4384 Assert (0 <= language && language < (int) LanguageCount);
4385 parserDefinition *const lang = LanguageTable [language].def;
4386 unsigned int i;
4388 if (lang->tagXpathTableCount > 0
4389 || lang->useMemoryStreamInput)
4391 verbose ("%s requires a memory stream for input\n", lang->name);
4392 return true;
4395 for (i = 0; i < lang->dependencyCount; i++)
4397 parserDependency *d = lang->dependencies + i;
4398 if (d->type == DEPTYPE_SUBPARSER &&
4399 ((subparser *)(d->data))->direction & SUBPARSER_SUB_RUNS_BASE)
4401 langType baseParser;
4402 baseParser = getNamedLanguage (d->upperParser, 0);
4403 if (doesParserRequireMemoryStream(baseParser))
4405 verbose ("%s/%s requires a memory stream for input\n", lang->name,
4406 LanguageTable[baseParser].def->name);
4407 return true;
4412 return false;
4415 extern bool parseFile (const char *const fileName)
4417 TRACE_ENTER_TEXT("Parsing file %s",fileName);
4418 bool bRet = parseFileWithMio (fileName, NULL, NULL);
4419 TRACE_LEAVE();
4420 return bRet;
4423 static bool parseMio (const char *const fileName, langType language, MIO* mio, time_t mtime, bool useSourceFileTagPath,
4424 void *clientData)
4426 bool tagFileResized = false;
4427 bool failureInOpenning = false;
4429 setupWriter (clientData);
4431 setupAnon ();
4433 initParserTrashBox ();
4435 tagFileResized = createTagsWithFallback (fileName, language, mio, mtime, &failureInOpenning);
4437 finiParserTrashBox ();
4439 teardownAnon ();
4441 if (useSourceFileTagPath && (!failureInOpenning))
4442 return teardownWriter (getSourceFileTagPath())? true: tagFileResized;
4443 else
4444 return teardownWriter(fileName);
4447 extern bool parseFileWithMio (const char *const fileName, MIO *mio,
4448 void *clientData)
4450 bool tagFileResized = false;
4451 langType language;
4452 struct GetLanguageRequest req = {
4453 .type = mio? GLR_REUSE: GLR_OPEN,
4454 .fileName = fileName,
4455 .mio = mio,
4457 memset (&req.mtime, 0, sizeof (req.mtime));
4459 language = getFileLanguageForRequest (&req);
4460 Assert (language != LANG_AUTO);
4462 if (Option.printLanguage)
4464 printGuessedParser (fileName, language);
4465 return tagFileResized;
4468 if (language == LANG_IGNORE)
4469 verbose ("ignoring %s (unknown language/language disabled)\n",
4470 fileName);
4471 else
4473 Assert(isLanguageEnabled (language));
4475 if (Option.filter && ! Option.interactive)
4476 openTagFile ();
4478 #ifdef HAVE_ICONV
4479 /* TODO: checkUTF8BOM can be used to update the encodings. */
4480 openConverter (getLanguageEncoding (language), Option.outputEncoding);
4481 #endif
4482 tagFileResized = parseMio (fileName, language, req.mio, req.mtime, true, clientData);
4483 if (Option.filter && ! Option.interactive)
4484 closeTagFile (tagFileResized);
4485 addTotals (1, 0L, 0L);
4487 #ifdef HAVE_ICONV
4488 closeConverter ();
4489 #endif
4492 if (req.type == GLR_OPEN && req.mio)
4493 mio_unref (req.mio);
4495 return tagFileResized;
4498 extern bool parseRawBuffer(const char *fileName, unsigned char *buffer,
4499 size_t bufferSize, const langType language, void *clientData)
4501 MIO *mio = NULL;
4502 bool r;
4504 if (buffer)
4505 mio = mio_new_memory (buffer, bufferSize, NULL, NULL);
4507 r = parseMio (fileName, language, mio, (time_t)0, false, clientData);
4509 if (buffer)
4510 mio_unref (mio);
4512 return r;
4515 static void matchLanguageMultilineRegexCommon (const langType language,
4516 bool (* func) (struct lregexControlBlock *, const vString* const),
4517 const vString* const allLines)
4519 subparser *tmp;
4521 func ((LanguageTable + language)->lregexControlBlock, allLines);
4522 foreachSubparser(tmp, true)
4524 langType t = getSubparserLanguage (tmp);
4525 enterSubparser (tmp);
4526 matchLanguageMultilineRegexCommon (t, func, allLines);
4527 leaveSubparser ();
4531 extern void matchLanguageMultilineRegex (const langType language,
4532 const vString* const allLines)
4534 matchLanguageMultilineRegexCommon(language, matchMultilineRegex, allLines);
4537 extern void matchLanguageMultitableRegex (const langType language,
4538 const vString* const allLines)
4540 matchLanguageMultilineRegexCommon(language, matchMultitableRegex, allLines);
4543 extern void processLanguageMultitableExtendingOption (langType language, const char *const parameter)
4545 const char* src;
4546 char* dist;
4547 const char *tmp;
4549 tmp = strchr(parameter, '+');
4551 if (!tmp)
4552 error (FATAL, "no separator(+) found: %s", parameter);
4554 if (tmp == parameter)
4555 error (FATAL, "the name of source table is empty in table extending: %s", parameter);
4557 src = tmp + 1;
4558 if (!*src)
4559 error (FATAL, "the name of dist table is empty in table extending: %s", parameter);
4561 dist = eStrndup(parameter, tmp - parameter);
4562 extendRegexTable(((LanguageTable + language)->lregexControlBlock), src, dist);
4563 eFree (dist);
4566 static bool lregexQueryParserAndSubparsers (const langType language, bool (* predicate) (struct lregexControlBlock *))
4568 bool r;
4569 subparser *tmp;
4571 r = predicate ((LanguageTable + language)->lregexControlBlock);
4572 if (!r)
4574 foreachSubparser(tmp, true)
4576 langType t = getSubparserLanguage (tmp);
4577 enterSubparser (tmp);
4578 r = lregexQueryParserAndSubparsers (t, predicate);
4579 leaveSubparser ();
4581 if (r)
4582 break;
4586 return r;
4589 extern bool hasLanguagePostRunRegexPatterns (const langType language)
4591 return lregexQueryParserAndSubparsers (language, regexIsPostRun);
4594 extern bool hasLanguageMultilineRegexPatterns (const langType language)
4596 return lregexQueryParserAndSubparsers (language, regexNeedsMultilineBuffer);
4599 static bool hasLanguageAnyRegexPatterns (const langType language)
4601 return lregexQueryParserAndSubparsers (language, lregexControlBlockHasAny);
4604 extern void addLanguageCallbackRegex (const langType language, const char *const regex, const char *const flags,
4605 const regexCallback callback, bool *disabled, void *userData)
4607 addCallbackRegex ((LanguageTable +language)->lregexControlBlock, regex, flags, callback, disabled, userData);
4610 extern bool doesLanguageExpectCorkInRegex (const langType language)
4612 bool hasScopeAction;
4614 pushLanguage (language);
4615 hasScopeAction = lregexQueryParserAndSubparsers (language, doesExpectCorkInRegex);
4616 popLanguage ();
4618 return hasScopeAction;
4621 extern void matchLanguageRegex (const langType language, const vString* const line, bool postrun)
4623 subparser *tmp;
4625 matchRegex ((LanguageTable + language)->lregexControlBlock, line, postrun);
4626 foreachSubparser(tmp, true)
4628 langType t = getSubparserLanguage (tmp);
4629 enterSubparser (tmp);
4630 matchLanguageRegex (t, line, postrun);
4631 leaveSubparser ();
4635 extern bool processLanguageRegexOption (langType language,
4636 enum regexParserType regptype,
4637 const char *const parameter)
4639 processTagRegexOption ((LanguageTable +language)->lregexControlBlock,
4640 regptype, parameter);
4642 return true;
4645 extern bool processTabledefOption (const char *const option, const char *const parameter)
4647 langType language;
4649 language = getLanguageComponentInOption (option, "_tabledef-");
4650 if (language == LANG_IGNORE)
4651 return false;
4653 if (parameter == NULL || parameter[0] == '\0')
4654 error (FATAL, "A parameter is needed after \"%s\" option", option);
4656 addRegexTable((LanguageTable +language)->lregexControlBlock, parameter);
4657 return true;
4660 extern void useRegexMethod (const langType language)
4662 parserDefinition* lang;
4664 Assert (0 <= language && language < (int) LanguageCount);
4665 lang = LanguageTable [language].def;
4666 lang->method |= METHOD_REGEX;
4669 static void useXpathMethod (const langType language)
4671 parserDefinition* lang;
4673 Assert (0 <= language && language < (int) LanguageCount);
4674 lang = LanguageTable [language].def;
4675 lang->method |= METHOD_XPATH;
4678 static void installTagRegexTable (const langType language)
4680 parserObject* parser;
4681 parserDefinition* lang;
4682 unsigned int i;
4684 Assert (0 <= language && language < (int) LanguageCount);
4685 parser = LanguageTable + language;
4686 lang = parser->def;
4689 if (lang->tagRegexTable != NULL)
4691 /* ctags_cli_main() calls initRegexOptscript ().
4692 * However, mini-geany deasn't call ctags_cli_main().
4693 * So we call initRegexOptscript () here.
4695 initRegexOptscript ();
4697 for (i = 0; i < lang->tagRegexCount; ++i)
4699 if (lang->tagRegexTable [i].mline)
4700 addTagMultiLineRegex (parser->lregexControlBlock,
4701 lang->tagRegexTable [i].regex,
4702 lang->tagRegexTable [i].name,
4703 lang->tagRegexTable [i].kinds,
4704 lang->tagRegexTable [i].flags,
4705 (lang->tagRegexTable [i].disabled));
4706 else
4707 addTagRegex (parser->lregexControlBlock,
4708 lang->tagRegexTable [i].regex,
4709 lang->tagRegexTable [i].name,
4710 lang->tagRegexTable [i].kinds,
4711 lang->tagRegexTable [i].flags,
4712 (lang->tagRegexTable [i].disabled));
4717 static void installKeywordTable (const langType language)
4719 parserDefinition* lang;
4720 unsigned int i;
4722 Assert (0 <= language && language < (int) LanguageCount);
4723 lang = LanguageTable [language].def;
4725 if (lang->keywordTable != NULL)
4727 for (i = 0; i < lang->keywordCount; ++i)
4728 addKeyword (lang->keywordTable [i].name,
4729 language,
4730 lang->keywordTable [i].id);
4734 static void installTagXpathTable (const langType language)
4736 parserDefinition* lang;
4737 unsigned int i, j;
4739 Assert (0 <= language && language < (int) LanguageCount);
4740 lang = LanguageTable [language].def;
4742 if (lang->tagXpathTableTable != NULL)
4744 for (i = 0; i < lang->tagXpathTableCount; ++i)
4745 for (j = 0; j < lang->tagXpathTableTable[i].count; ++j)
4746 addTagXpath (language, lang->tagXpathTableTable[i].table + j);
4747 useXpathMethod (language);
4751 static void uninstallTagXpathTable (const langType language)
4753 parserDefinition* lang;
4754 unsigned int i, j;
4756 Assert (0 <= language && language < (int) LanguageCount);
4757 lang = LanguageTable [language].def;
4759 if (lang->tagXpathTableTable != NULL)
4761 for (i = 0; i < lang->tagXpathTableCount; ++i)
4762 for (j = 0; j < lang->tagXpathTableTable[i].count; ++j)
4763 removeTagXpath (language, lang->tagXpathTableTable[i].table + j);
4767 const tagXpathTableTable *getXpathTableTable (const langType language, unsigned int nth)
4769 parserDefinition* lang;
4771 Assert (0 <= language && language < (int) LanguageCount);
4772 lang = LanguageTable [language].def;
4774 Assert (nth < lang->tagXpathTableCount);
4775 return lang->tagXpathTableTable + nth;
4778 extern unsigned int getXpathFileSpecCount (const langType language)
4780 parserDefinition* lang;
4782 Assert (0 <= language && language < (int) LanguageCount);
4783 lang = LanguageTable [language].def;
4785 return lang->xpathFileSpecCount;
4788 extern xpathFileSpec* getXpathFileSpec (const langType language, unsigned int nth)
4790 parserDefinition* lang;
4792 Assert (0 <= language && language < (int) LanguageCount);
4793 lang = LanguageTable [language].def;
4795 Assert (nth < lang->xpathFileSpecCount);
4796 return lang->xpathFileSpecs + nth;
4799 extern bool makeKindSeparatorsPseudoTags (const langType language,
4800 const ptagDesc *pdesc)
4802 parserObject* parser;
4803 parserDefinition* lang;
4804 struct kindControlBlock *kcb;
4805 kindDefinition *kind;
4806 unsigned int kindCount;
4807 unsigned int i, j;
4809 bool r = false;
4811 Assert (0 <= language && language < (int) LanguageCount);
4812 parser = LanguageTable + language;
4813 lang = parser->def;
4814 kcb = parser->kindControlBlock;
4815 kindCount = countKinds(kcb);
4817 if (kindCount == 0)
4818 return r;
4820 for (i = 0; i < kindCount; ++i)
4822 kind = getKind (kcb, i);
4823 for (j = 0; j < kind->separatorCount; ++j)
4825 char name[3] = {[1] = '\0', [2] = '\0'};
4826 const kindDefinition *upperKind;
4827 const scopeSeparator *sep;
4829 sep = kind->separators + j;
4831 if (sep->parentKindIndex == KIND_WILDCARD_INDEX)
4833 name[0] = KIND_WILDCARD_LETTER;
4834 name[1] = kind->letter;
4836 else if (sep->parentKindIndex == KIND_GHOST_INDEX)
4838 /* This is root separator: no upper item is here. */
4839 name[0] = kind->letter;
4841 else
4843 upperKind = getLanguageKind (language,
4844 sep->parentKindIndex);
4845 if (!upperKind)
4846 continue;
4848 name[0] = upperKind->letter;
4849 name[1] = kind->letter;
4853 r = writePseudoTag (pdesc, sep->separator? sep->separator: "",
4854 name, lang->name) || r;
4858 return r;
4861 struct makeKindDescriptionPseudoTagData {
4862 const char* langName;
4863 const ptagDesc *pdesc;
4864 bool written;
4867 static bool makeKindDescriptionPseudoTag (kindDefinition *kind,
4868 void *user_data)
4870 struct makeKindDescriptionPseudoTagData *data = user_data;
4871 vString *letter_and_name;
4873 letter_and_name = vStringNew ();
4875 vStringPut (letter_and_name, kind -> letter);
4876 vStringPut (letter_and_name, ',');
4877 vStringCatS (letter_and_name, kind -> name);
4879 data->written |= writePseudoTag (data->pdesc, vStringValue (letter_and_name),
4880 kind->description? kind->description: kind->name,
4881 data->langName);
4883 vStringDelete (letter_and_name);
4885 return false;
4888 static bool makeRoleDescriptionPseudoTag (kindDefinition *kind,
4889 roleDefinition *role,
4890 void *user_data)
4892 struct makeKindDescriptionPseudoTagData *data = user_data;
4894 vString *parser_and_kind_name = vStringNewInit (data->langName);
4895 vStringCatS (parser_and_kind_name, PSEUDO_TAG_SEPARATOR);
4896 vStringCatS (parser_and_kind_name, kind->name);
4898 data->written |= writePseudoTag (data->pdesc, role->name,
4899 role->description? role->description: role->name,
4900 vStringValue (parser_and_kind_name));
4902 vStringDelete (parser_and_kind_name);
4904 return false;
4907 extern bool makeKindDescriptionsPseudoTags (const langType language,
4908 const ptagDesc *pdesc)
4910 parserObject *parser;
4911 struct kindControlBlock *kcb;
4912 parserDefinition* lang;
4913 kindDefinition *kind;
4914 unsigned int kindCount, i;
4915 struct makeKindDescriptionPseudoTagData data;
4917 Assert (0 <= language && language < (int) LanguageCount);
4918 parser = LanguageTable + language;
4919 kcb = parser->kindControlBlock;
4920 lang = parser->def;
4922 kindCount = countKinds(kcb);
4924 data.langName = lang->name;
4925 data.pdesc = pdesc;
4926 data.written = false;
4928 for (i = 0; i < kindCount; ++i)
4930 if (!isLanguageKindEnabled (language, i))
4931 continue;
4933 kind = getKind (kcb, i);
4934 if (language == ctagsSelfTestLang
4935 && (kind == NULL || kind->name == NULL))
4937 /* The Self test parser may have broken kinds.
4938 * Let's skip it. */
4939 continue;
4941 makeKindDescriptionPseudoTag (kind, &data);
4944 return data.written;
4947 static bool makeFieldDescriptionPseudoTag (const langType language,
4948 fieldType f,
4949 const ptagDesc *pdesc)
4951 const char *name = getFieldName (f);
4953 if (name == NULL || name [0] == '\0')
4954 return false;
4956 return writePseudoTag (pdesc, name,
4957 getFieldDescription (f),
4958 language == LANG_IGNORE? NULL: getLanguageName (language));
4961 extern bool makeFieldDescriptionsPseudoTags (const langType language,
4962 const ptagDesc *pdesc)
4964 bool written = false;
4965 for (unsigned int i = 0; i < countFields (); i++)
4967 if (getFieldLanguage (i) == language
4968 && isFieldEnabled (i))
4970 if (makeFieldDescriptionPseudoTag (language, i, pdesc))
4971 written = true;
4974 return written;
4977 static bool makeExtraDescriptionPseudoTag (const langType language,
4978 xtagType x,
4979 const ptagDesc *pdesc)
4981 const char *name = getXtagName (x);
4983 if (name == NULL || name [0] == '\0')
4984 return false;
4986 return writePseudoTag (pdesc, name,
4987 getXtagDescription (x),
4988 language == LANG_IGNORE? NULL: getLanguageName (language));
4991 extern bool makeExtraDescriptionsPseudoTags (const langType language,
4992 const ptagDesc *pdesc)
4994 bool written = false;
4995 for (unsigned int i = 0; i < countXtags (); i++)
4997 if (getXtagLanguage (i) == language
4998 && isXtagEnabled (i))
5000 if (makeExtraDescriptionPseudoTag (language, i, pdesc))
5001 written = true;
5004 return written;
5007 extern bool makeRoleDescriptionsPseudoTags (const langType language,
5008 const ptagDesc *pdesc)
5010 parserObject *parser;
5011 struct kindControlBlock *kcb;
5012 parserDefinition* lang;
5013 kindDefinition *kind;
5014 struct makeKindDescriptionPseudoTagData data;
5016 Assert (0 <= language && language < (int) LanguageCount);
5017 parser = LanguageTable + language;
5018 kcb = parser->kindControlBlock;
5019 lang = parser->def;
5021 unsigned int kindCount = countKinds(kcb);
5023 data.langName = lang->name;
5024 data.pdesc = pdesc;
5025 data.written = false;
5027 for (unsigned int i = 0; i < kindCount; ++i)
5029 if (!isLanguageKindEnabled (language, i))
5030 continue;
5032 kind = getKind (kcb, i);
5034 unsigned int roleCount = countRoles (kcb, i);
5035 for (unsigned int j = 0; j < roleCount; ++j)
5037 if (isRoleEnabled (kcb, i, j))
5039 roleDefinition *role = getRole (kcb, i, j);
5040 makeRoleDescriptionPseudoTag (kind, role, &data);
5045 return data.written;
5048 extern unsigned int getLanguageVersionCurrent (const langType language)
5050 parserObject *parser;
5051 parserDefinition* lang;
5052 Assert (0 <= language && language < (int) LanguageCount);
5053 parser = LanguageTable + language;
5054 lang = parser->def;
5055 return lang->versionCurrent;
5058 extern unsigned int getLanguageVersionAge (const langType language)
5060 parserObject *parser;
5061 parserDefinition* lang;
5062 Assert (0 <= language && language < (int) LanguageCount);
5063 parser = LanguageTable + language;
5064 lang = parser->def;
5065 return lang->versionAge;
5069 * Copyright (c) 2016, Szymon Tomasz Stefanek
5071 * This source code is released for free distribution under the terms of the
5072 * GNU General Public License version 2 or (at your option) any later version.
5074 * Anonymous name generator
5076 static ptrArray *parsersUsedInCurrentInput;
5078 static void setupAnon (void)
5080 parsersUsedInCurrentInput = ptrArrayNew (NULL);
5083 static void teardownAnon (void)
5085 ptrArrayDelete (parsersUsedInCurrentInput);
5088 static void anonResetMaybe (parserObject *parser)
5090 if (ptrArrayHas (parsersUsedInCurrentInput, parser))
5091 return;
5093 parser -> anonymousIdentiferId = 0;
5094 ptrArrayAdd (parsersUsedInCurrentInput, parser);
5097 static unsigned int anonHash(const unsigned char *str)
5099 unsigned int hash = 5381;
5100 int c;
5102 while((c = *str++))
5103 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
5105 return hash ;
5108 extern void anonHashString (const char *filename, char buf[9])
5110 sprintf(buf, "%08x", anonHash((const unsigned char *)filename));
5113 extern void anonConcatFull (vString *buffer, langType lang, int kind)
5115 anonGenerateFull (buffer, NULL, lang, kind);
5118 extern void anonGenerateFull (vString *buffer, const char *prefix, langType lang, int kind)
5120 Assert(lang != LANG_IGNORE);
5121 parserObject* parser = LanguageTable + ((lang == LANG_AUTO)? getInputLanguage (): lang);
5122 parser -> anonymousIdentiferId ++;
5124 char szNum[32];
5125 char buf [9];
5127 if (prefix)
5128 vStringCopyS(buffer, prefix);
5130 anonHashString (getInputFileName(), buf);
5131 sprintf(szNum,"%s%02x%02x",buf,parser -> anonymousIdentiferId, kind);
5132 vStringCatS(buffer,szNum);
5135 extern vString *anonGenerateNewFull (const char *prefix, langType lang, int kind)
5137 vString *buffer = vStringNew ();
5139 anonGenerateFull (buffer, prefix, lang, kind);
5140 return buffer;
5143 extern bool applyLanguageParam (const langType language, const char *name, const char *args)
5145 Assert (0 <= language && language < (int) LanguageCount);
5147 initializeParserOne (language);
5148 return applyParam (LanguageTable [language].paramControlBlock, name, args);
5151 extern subparser *getNextSubparser(subparser *last,
5152 bool includingNoneCraftedParser)
5154 langType lang = getInputLanguage ();
5155 parserObject *parser = LanguageTable + lang;
5156 subparser *r;
5157 langType t;
5159 if (last == NULL)
5160 r = getFirstSubparser(parser->slaveControlBlock);
5161 else
5162 r = last->next;
5164 if (r == NULL)
5165 return r;
5167 t = getSubparserLanguage(r);
5168 if (isLanguageEnabled (t) &&
5169 (includingNoneCraftedParser
5170 || ((((LanguageTable + t)->def->method) & METHOD_NOT_CRAFTED) == 0)))
5171 return r;
5172 else
5173 return getNextSubparser (r, includingNoneCraftedParser);
5176 extern slaveParser *getNextSlaveParser(slaveParser *last)
5178 langType lang = getInputLanguage ();
5179 parserObject *parser = LanguageTable + lang;
5180 slaveParser *r;
5182 if (last == NULL)
5183 r = getFirstSlaveParser(parser->slaveControlBlock);
5184 else
5185 r = last->next;
5187 return r;
5190 extern void scheduleRunningBaseparser (int dependencyIndex)
5192 langType current = getInputLanguage ();
5193 parserObject *current_pobj = LanguageTable + current;
5194 parserDefinition *current_parser = current_pobj->def;
5195 parserDependency *dep = NULL;
5197 current_pobj->justRunForSchedulingBase = 1;
5199 if (dependencyIndex == RUN_DEFAULT_SUBPARSERS)
5201 for (unsigned int i = 0; i < current_parser->dependencyCount; ++i)
5202 if (current_parser->dependencies[i].type == DEPTYPE_SUBPARSER)
5204 dep = current_parser->dependencies + i;
5205 break;
5208 else
5209 dep = current_parser->dependencies + dependencyIndex;
5211 if (dep == NULL)
5212 return;
5214 const char *base_name = dep->upperParser;
5215 langType base = getNamedLanguage (base_name, 0);
5216 parserObject *base_parser = LanguageTable + base;
5218 if (dependencyIndex == RUN_DEFAULT_SUBPARSERS)
5219 useDefaultSubparsers(base_parser->slaveControlBlock);
5220 else
5221 useSpecifiedSubparser (base_parser->slaveControlBlock,
5222 dep->data);
5224 if (!isLanguageEnabled (base))
5226 enableLanguage (base, true);
5227 base_parser->dontEmit = true;
5228 verbose ("force enable \"%s\" as base parser\n", base_parser->def->name);
5232 subparser *tmp;
5234 verbose ("scheduleRunningBaseparser %s with subparsers: ", base_name);
5235 pushLanguage (base);
5236 foreachSubparser(tmp, true)
5238 langType t = getSubparserLanguage (tmp);
5239 verbose ("%s ", getLanguageName (t));
5241 popLanguage ();
5242 verbose ("\n");
5246 makePromise(base_name, THIN_STREAM_SPEC);
5249 extern bool isParserMarkedNoEmission (void)
5251 langType lang = getInputLanguage();
5252 parserObject *parser = LanguageTable + lang;
5254 return parser->dontEmit;
5258 extern subparser* getSubparserRunningBaseparser (void)
5260 langType current = getInputLanguage ();
5261 parserObject *current_parser = LanguageTable + current;
5262 subparser *s = getFirstSubparser (current_parser->slaveControlBlock);
5264 if (s && s->schedulingBaseparserExplicitly)
5265 return s;
5266 else
5267 return NULL;
5270 extern void printLanguageSubparsers (const langType language,
5271 bool withListHeader, bool machinable, FILE *fp)
5273 for (int i = 0; i < (int) LanguageCount; i++)
5274 initializeParserOne (i);
5276 struct colprintTable * table = subparserColprintTableNew();
5277 parserObject *parser;
5279 if (language == LANG_AUTO)
5281 for (int i = 0; i < (int) LanguageCount; i++)
5283 parser = LanguageTable + i;
5284 if (parser->def->invisible)
5285 continue;
5287 subparserColprintAddSubparsers (table,
5288 parser->slaveControlBlock);
5291 else
5293 parser = (LanguageTable + language);
5294 subparserColprintAddSubparsers (table,
5295 parser->slaveControlBlock);
5298 subparserColprintTablePrint (table,
5299 withListHeader, machinable,
5300 fp);
5301 colprintTableDelete (table);
5304 extern void printLangdefFlags (bool withListHeader, bool machinable, FILE *fp)
5306 struct colprintTable * table;
5308 table = flagsColprintTableNew ();
5310 flagsColprintAddDefinitions (table, PreLangDefFlagDef, ARRAY_SIZE (PreLangDefFlagDef));
5312 flagsColprintTablePrint (table, withListHeader, machinable, fp);
5313 colprintTableDelete(table);
5316 extern void printKinddefFlags (bool withListHeader, bool machinable, FILE *fp)
5318 struct colprintTable * table;
5320 table = flagsColprintTableNew ();
5322 flagsColprintAddDefinitions (table, PreKindDefFlagDef, ARRAY_SIZE (PreKindDefFlagDef));
5324 flagsColprintTablePrint (table, withListHeader, machinable, fp);
5325 colprintTableDelete(table);
5328 extern void printLanguageMultitableStatistics (langType language)
5330 parserObject* const parser = LanguageTable + language;
5331 printMultitableStatistics (parser->lregexControlBlock);
5334 extern void addLanguageRegexTable (const langType language, const char *name)
5336 parserObject* const parser = LanguageTable + language;
5337 addRegexTable (parser->lregexControlBlock, name);
5340 extern void addLanguageTagMultiTableRegex(const langType language,
5341 const char* const table_name,
5342 const char* const regex,
5343 const char* const name, const char* const kinds, const char* const flags,
5344 bool *disabled)
5346 parserObject* const parser = LanguageTable + language;
5347 addTagMultiTableRegex (parser->lregexControlBlock, table_name, regex,
5348 name, kinds, flags, disabled);
5351 extern void addLanguageOptscriptToHook (langType language, enum scriptHook hook, const char *const src)
5353 addOptscriptToHook (LanguageTable [language].lregexControlBlock, hook, src);
5356 static bool processHookOption (const char *const option, const char *const parameter, const char *prefix,
5357 enum scriptHook hook)
5359 langType language = getLanguageComponentInOption (option, prefix);
5360 if (language == LANG_IGNORE)
5361 return false;
5363 if (parameter == NULL || parameter[0] == '\0')
5364 error (FATAL, "A parameter is needed after \"%s\" option", option);
5366 const char * code = flagsEval (parameter, NULL, 0, NULL);
5367 if (code == NULL)
5368 error (FATAL, "Cannot recognized a code block surrounded by `{{' and `}}' after \"%s\" option", option);
5369 addLanguageOptscriptToHook (language, hook, code);
5371 return true;
5374 extern bool processPreludeOption (const char *const option, const char *const parameter)
5376 return processHookOption (option, parameter, "_prelude-", SCRIPT_HOOK_PRELUDE);
5379 extern bool processSequelOption (const char *const option, const char *const parameter)
5381 return processHookOption (option, parameter, "_sequel-", SCRIPT_HOOK_SEQUEL);
5384 extern bool processPretendOption (const char *const option, const char *const parameter)
5386 langType new_language, old_language;
5388 #define pretendOptionPrefix "_pretend-"
5389 new_language = getLanguageComponentInOptionFull (option, pretendOptionPrefix, true);
5390 if (new_language == LANG_IGNORE)
5391 return false;
5393 if (parameter == NULL || parameter[0] == '\0')
5394 error (FATAL, "A parameter is needed after \"%s\" option", option);
5396 old_language = getNamedLanguageFull (parameter, 0, true, false);
5397 if (old_language == LANG_IGNORE)
5398 error (FATAL, "Unknown language \"%s\" in option \"--%s=%s\"",
5399 parameter, option, parameter);
5401 if (LanguageTable [new_language].pretendingAsLanguage != LANG_IGNORE)
5403 error (FATAL, "%s parser pretends as %s already\n",
5404 getLanguageNameFull (new_language, true),
5405 getLanguageNameFull (LanguageTable [new_language].pretendingAsLanguage, true));
5407 if (LanguageTable [old_language].pretendedAsLanguage != LANG_IGNORE)
5409 error (FATAL, "%s parser is pretended as %s already\n",
5410 getLanguageNameFull (old_language, true),
5411 getLanguageNameFull (LanguageTable [old_language].pretendedAsLanguage, true));
5414 verbose ("%s pretends %s\n",
5415 getLanguageNameFull (new_language, true),
5416 getLanguageNameFull (old_language, true));
5418 LanguageTable [new_language].pretendingAsLanguage = old_language;
5419 LanguageTable [old_language].pretendedAsLanguage = new_language;
5421 verbose ("force enabling %s\n",
5422 getLanguageNameFull (new_language, true));
5423 enableLanguage (new_language, true);
5425 verbose ("force disabling %s\n",
5426 getLanguageNameFull (old_language, true));
5427 enableLanguage (old_language, false);
5429 return true;
5432 extern unsigned int getLanguageCorkUsage (langType lang)
5434 parserObject* const parser = LanguageTable + lang;
5435 return parserCorkFlags (parser->def);
5439 * The universal fallback parser.
5440 * If any parser doesn't handle the input, this parser is
5441 * used for the input when --languages=+Unknown is given.
5442 * writer-etags enables this parser implicitly.
5444 static parserDefinition *FallbackParser (void)
5446 parserDefinition *const def = parserNew ("Unknown");
5447 def->extensions = NULL;
5448 def->kindTable = NULL;
5449 def->kindCount = 0;
5451 /* A user can extend this parser with --regex-Unknown=...
5452 * or --langdef=MyParser{base=Unknown}.
5454 * TODO: if following conditions are met, dontFindTags()
5455 * defined below can be used.
5456 * - any regex pattern is not defined,
5457 * - any sub parser is not defined, and
5458 * - end: field is not enabled.
5460 def->parser = findRegexTags;
5461 def->enabled = 0;
5462 def->method = METHOD_REGEX;
5463 return def;
5467 * A dummy parser for printing pseudo tags in xref output
5469 static void dontFindTags (void)
5473 static kindDefinition CtagsKinds[] = {
5474 {true, 'p', "ptag", "pseudo tags"},
5477 static parserDefinition *CTagsParser (void)
5479 parserDefinition *const def = parserNew ("UniversalCtags");
5480 def->extensions = NULL;
5481 def->kindTable = CtagsKinds;
5482 def->kindCount = ARRAY_SIZE(CtagsKinds);
5483 def->parser = dontFindTags;
5484 def->invisible = true;
5485 return def;
5489 * A parser for CTagsSelfTest (CTST)
5491 #define SELF_TEST_PARSER "CTagsSelfTest"
5492 #if defined(DEBUG) && defined(HAVE_SECCOMP)
5493 extern void getppid(void);
5494 #endif
5496 static bool CTST_GatherStats;
5497 static int CTST_num_handled_char;
5499 typedef enum {
5500 K_BROKEN,
5501 K_NO_LETTER,
5502 K_NO_LONG_NAME,
5503 K_NOTHING_SPECIAL,
5504 K_GUEST_BEGINNING,
5505 K_GUEST_END,
5506 #if defined(DEBUG) && defined(HAVE_SECCOMP)
5507 K_CALL_GETPPID,
5508 #endif
5509 K_QUIT,
5510 K_DISABLED,
5511 K_ENABLED,
5512 K_ROLES,
5513 K_ROLES_DISABLED,
5514 K_FIELD_TESTING,
5515 K_TRIGGER_NOTICE,
5516 KIND_COUNT
5517 } CTST_Kind;
5519 typedef enum {
5520 R_BROKEN_REF,
5521 } CTST_BrokenRole;
5523 static roleDefinition CTST_BrokenRoles [] = {
5524 {true, "broken", "broken" },
5527 typedef enum {
5528 R_DISABLED_KIND_DISABLED_ROLE,
5529 R_DISABLED_KIND_ENABLED_ROLE,
5530 } CTST_DisabledKindRole;
5532 static roleDefinition CTST_DisabledKindRoles [] = {
5533 { false, "disabled", "disabled role attached to disabled kind" },
5534 { true, "enabled", "enabled role attached to disabled kind" },
5537 typedef enum {
5538 R_ENABLED_KIND_DISABLED_ROLE,
5539 R_ENABLED_KIND_ENABLED_ROLE,
5540 } CTST_EnabledKindRole;
5542 static roleDefinition CTST_EnabledKindRoles [] = {
5543 { false, "disabled", "disabled role attached to enabled kind" },
5544 { true, "enabled", "enabled role attached to enabled kind" },
5547 typedef enum {
5548 R_ROLES_KIND_A_ROLE,
5549 R_ROLES_KIND_B_ROLE,
5550 R_ROLES_KIND_C_ROLE,
5551 R_ROLES_KIND_D_ROLE,
5552 } CTST_RolesKindRole;
5554 static roleDefinition CTST_RolesKindRoles [] = {
5555 { true, "a", "A role" },
5556 { true, "b", "B role" },
5557 { false, "c", "C role" },
5558 { true, "d", "D role" },
5561 typedef enum {
5562 R_ROLES_DISABLED_KIND_A_ROLE,
5563 R_ROLES_DISABLED_KIND_B_ROLE,
5564 } CTST_RolesDisableKindRole;
5567 static roleDefinition CTST_RolesDisabledKindRoles [] = {
5568 { true, "A", "A role" },
5569 { true, "B", "B role" },
5572 static kindDefinition CTST_Kinds[KIND_COUNT] = {
5573 /* `a' is reserved for kinddef testing */
5574 {true, 'b', "broken tag", "name with unwanted characters",
5575 .referenceOnly = false, ATTACH_ROLES (CTST_BrokenRoles) },
5576 {true, KIND_NULL_LETTER, "no letter", "kind with no letter"
5577 /* use '@' when testing. */
5579 {true, 'L', NULL, "kind with no long name" },
5580 {true, 'N', "nothingSpecial", "emit a normal tag" },
5581 {true, 'B', NULL, "beginning of an area for a guest" },
5582 {true, 'E', NULL, "end of an area for a guest" },
5583 #if defined(DEBUG) && defined(HAVE_SECCOMP)
5584 {true, 'P', "callGetPPid", "trigger calling getppid(2) that seccomp sandbox disallows"},
5585 #endif
5586 {true, 'Q', "quit", "stop the parsing"},
5587 {false,'d', "disabled", "a kind disabled by default",
5588 .referenceOnly = false, ATTACH_ROLES (CTST_DisabledKindRoles)},
5589 {true, 'e', "enabled", "a kind enabled by default",
5590 .referenceOnly = false, ATTACH_ROLES (CTST_EnabledKindRoles)},
5591 {true, 'r', "roles", "emit a tag with multi roles",
5592 .referenceOnly = true, ATTACH_ROLES (CTST_RolesKindRoles)},
5593 {false, 'R', "rolesDisabled", "emit a tag with multi roles(disabled by default)",
5594 .referenceOnly = true, ATTACH_ROLES (CTST_RolesDisabledKindRoles)},
5595 {true, 'f', "fieldMaker", "tag for testing field:" },
5596 {true, 'n', "triggerNotice", "trigger notice output"},
5599 typedef enum {
5600 F_BOOLEAN_FIELD,
5601 F_BOOLEAN_AND_STRING_FIELD,
5602 COUNT_FIELD
5603 } CTSTField;
5605 static fieldDefinition CTSTFields[COUNT_FIELD] = {
5606 { .name = "bField",
5607 .description = "field for testing boolean type",
5608 .dataType = FIELDTYPE_BOOL,
5609 .enabled = true,
5611 { .name = "sbField",
5612 .description = "field for testing string|boolean type",
5613 .dataType = FIELDTYPE_STRING|FIELDTYPE_BOOL,
5614 .enabled = true,
5618 static void createCTSTTags (void)
5620 int i;
5621 const unsigned char *line;
5622 tagEntryInfo e;
5624 unsigned long lb = 0;
5625 unsigned long le = 0;
5627 int found_enabled_disabled[2] = {0, 0};
5629 bool quit = false;
5631 TRACE_ENTER_TEXT("Parsing starts");
5633 while (!quit && (line = readLineFromInputFile ()) != NULL)
5635 int c = line[0];
5637 for (i = 0; i < KIND_COUNT; i++)
5638 if ((c == CTST_Kinds[i].letter && i != K_NO_LETTER)
5639 || (c == '@' && i == K_NO_LETTER))
5641 if (CTST_GatherStats)
5642 CTST_num_handled_char++;
5644 switch (i)
5646 case K_BROKEN:
5647 initTagEntry (&e, "one\nof\rbroken\tname", i);
5648 e.extensionFields.scopeKindIndex = K_BROKEN;
5649 e.extensionFields.scopeName = "\\Broken\tContext";
5650 makeTagEntry (&e);
5651 initTagEntry (&e, "only\nnewline", i);
5652 makeTagEntry (&e);
5653 initTagEntry (&e, "only\ttab", i);
5654 makeTagEntry (&e);
5655 initTagEntry (&e, "newline-in-scope", i);
5656 e.extensionFields.scopeKindIndex = K_BROKEN;
5657 e.extensionFields.scopeName = "parent\nscope";
5658 makeTagEntry (&e);
5659 initTagEntry (&e, "tab-in-scope", i);
5660 e.extensionFields.scopeKindIndex = K_BROKEN;
5661 e.extensionFields.scopeName = "parent\tscope";
5662 makeTagEntry (&e);
5663 break;
5664 case K_NO_LETTER:
5665 initTagEntry (&e, "abnormal kindDefinition testing (no letter)", i);
5666 makeTagEntry (&e);
5667 break;
5668 case K_NO_LONG_NAME:
5669 initTagEntry (&e, "abnormal kindDefinition testing (no long name)", i);
5670 makeTagEntry (&e);
5671 break;
5672 case K_NOTHING_SPECIAL:
5673 if (!lb)
5675 initTagEntry (&e, "NOTHING_SPECIAL", i);
5676 makeTagEntry (&e);
5678 break;
5679 case K_GUEST_BEGINNING:
5680 lb = getInputLineNumber ();
5681 break;
5682 case K_GUEST_END:
5683 le = getInputLineNumber ();
5684 makePromise (SELF_TEST_PARSER, lb + 1, 0, le, 0, lb + 1);
5685 break;
5686 #if defined(DEBUG) && defined(HAVE_SECCOMP)
5687 case K_CALL_GETPPID:
5688 getppid();
5689 break;
5690 #endif
5691 case K_QUIT:
5692 quit = true;
5693 break;
5694 case K_DISABLED:
5695 case K_ENABLED:
5697 int role;
5698 char *name;
5699 if (found_enabled_disabled[i == K_DISABLED]++ == 0)
5701 role = ROLE_DEFINITION_INDEX;
5702 name = (i == K_DISABLED)
5703 ? "disable-kind-no-role"
5704 : "enabled-kind-no-role";
5706 else if (found_enabled_disabled[i == K_DISABLED]++ == 1)
5708 role = (i == K_DISABLED)
5709 ? R_DISABLED_KIND_DISABLED_ROLE
5710 : R_ENABLED_KIND_DISABLED_ROLE;
5711 name = (i == K_DISABLED)
5712 ? "disable-kind-disabled-role"
5713 : "enabled-kind-disabled-role";
5715 else
5717 role = (i == K_DISABLED)
5718 ? R_DISABLED_KIND_ENABLED_ROLE
5719 : R_ENABLED_KIND_ENABLED_ROLE;
5720 name = (i == K_DISABLED)
5721 ? "disable-kind-enabled-role"
5722 : "enabled-kind-enabled-role";
5724 initRefTagEntry (&e, name, i, role);
5725 makeTagEntry (&e);
5726 break;
5728 case K_ROLES:
5730 char *name = "multiRolesTarget";
5731 int qindex;
5732 tagEntryInfo *qe;
5734 initTagEntry (&e, name, i);
5735 assignRole(&e, R_ROLES_KIND_A_ROLE);
5736 assignRole(&e, R_ROLES_KIND_C_ROLE);
5737 assignRole(&e, R_ROLES_KIND_D_ROLE);
5738 qindex = makeTagEntry (&e);
5739 qe = getEntryInCorkQueue (qindex);
5740 if (qe)
5741 assignRole(qe, R_ROLES_KIND_B_ROLE);
5742 break;
5744 case K_ROLES_DISABLED:
5746 char *name = "multiRolesDisabledTarget";
5748 initRefTagEntry (&e, name, i, R_ROLES_DISABLED_KIND_A_ROLE);
5749 makeTagEntry (&e);
5750 initRefTagEntry (&e, name, i, R_ROLES_DISABLED_KIND_B_ROLE);
5751 makeTagEntry (&e);
5752 break;
5754 case K_FIELD_TESTING:
5756 char c = 'a';
5757 char name []= {'\0', 't', 'a', 'g', '\0' };
5759 name [0] = c++;
5760 initTagEntry (&e, name, i);
5761 attachParserField (&e,
5762 CTSTFields[F_BOOLEAN_FIELD].ftype, "");
5763 makeTagEntry (&e);
5765 name [0] = c++;
5766 initTagEntry (&e, name, i);
5767 makeTagEntry (&e);
5769 name [0] = c++;
5770 initTagEntry (&e, name, i);
5771 attachParserField (&e,
5772 CTSTFields[F_BOOLEAN_AND_STRING_FIELD].ftype, "val");
5773 makeTagEntry (&e);
5775 name [0] = c++;
5776 initTagEntry (&e, name, i);
5777 attachParserField (&e,
5778 CTSTFields[F_BOOLEAN_AND_STRING_FIELD].ftype, "");
5779 makeTagEntry (&e);
5781 break;
5783 case K_TRIGGER_NOTICE:
5784 notice ("notice output for testing: %s", CTST_Kinds [i].name);
5785 break;
5788 if (quit)
5789 break;
5793 TRACE_LEAVE();
5796 static void initStatsCTST (langType lang CTAGS_ATTR_UNUSED)
5798 CTST_GatherStats = true;
5801 static void printStatsCTST (langType lang CTAGS_ATTR_UNUSED)
5803 fprintf (stderr, "The number of handled chars: %d\n",
5804 CTST_num_handled_char);
5807 static void initCTST (langType language)
5809 ctagsSelfTestLang = language;
5812 static parserDefinition *CTagsSelfTestParser (void)
5814 static const char *const extensions[] = { NULL };
5815 parserDefinition *const def = parserNew (SELF_TEST_PARSER);
5816 def->extensions = extensions;
5817 def->kindTable = CTST_Kinds;
5818 def->kindCount = KIND_COUNT;
5819 def->parser = createCTSTTags;
5820 def->initialize = initCTST;
5821 def->invisible = true;
5822 def->useMemoryStreamInput = true;
5823 def->useCork = CORK_QUEUE;
5824 def->initStats = initStatsCTST;
5825 def->printStats = printStatsCTST;
5826 def->fieldTable = CTSTFields;
5827 def->fieldCount = ARRAY_SIZE (CTSTFields);
5829 return def;