/* * $Id$ * * Copyright (c) 2000-2005, Darren Hiebert * * This source code is released for free distribution under the terms of the * GNU General Public License. * * This module contains functions for generating tags for makefiles. */ /* * INCLUDE FILES */ #include "general.h" /* must always come first */ #include #include #include #include "options.h" #include "parse.h" #include "read.h" #include "vstring.h" /* * DATA DEFINITIONS */ typedef enum { K_MACRO, K_TARGET } shKind; static kindOption MakeKinds [] = { { TRUE, 'm', "macro", "macros"}, { TRUE, 't', "function", "targets"} }; /* * FUNCTION DEFINITIONS */ static int nextChar (void) { int c = fileGetc (); if (c == '\\') { c = fileGetc (); if (c == '\n') c = fileGetc (); } return c; } static void skipLine (void) { int c; do c = nextChar (); while (c != EOF && c != '\n'); if (c == '\n') fileUngetc (c); } static int skipToNonWhite (void) { int c; do c = nextChar (); while (c != '\n' && isspace (c)); return c; } static boolean isIdentifier (int c) { return (boolean)(c != '\0' && (isalnum (c) || strchr (".-_/", c) != NULL)); } static boolean isSpecialTarget (vString *const name) { size_t i = 0; /* All special targets begin with '.'. */ if (vStringChar (name, i++) != '.') { return FALSE; } while (i < vStringLength (name)) { char ch = vStringChar (name, i++); if (ch != '_' && !isupper (ch)) { return FALSE; } } return TRUE; } static void newTarget (vString *const name) { /* Ignore GNU Make's "special targets". */ if (isSpecialTarget (name)) { return; } makeSimpleTag (name, MakeKinds, K_TARGET); } static void newMacro (vString *const name) { makeSimpleTag (name, MakeKinds, K_MACRO); } static void newMacroFromDefine (vString *const name) { /* name is something like "define JAVAHPP_RULE", find the space and jump to the next char */ vStringCopyS (name, strchr (vStringValue (name), ' ') + 1); makeSimpleTag (name, MakeKinds, K_MACRO); } static void readIdentifier (const int first, vString *const id) { int c = first; int c_prev = first; int c_next = first; vStringClear (id); while (isIdentifier (c) || c == ' ') { c_next = nextChar (); if (c == ' ') { /* add the space character only if the previous and * next character are valid identifiers */ if (isIdentifier (c_prev) && isIdentifier (c_next)) vStringPut (id, c); } else { vStringPut (id, c); } c_prev = c; c = c_next; } fileUngetc (c); vStringTerminate (id); } static void skipToMatch (const char *const pair) { const int begin = pair [0], end = pair [1]; const unsigned long inputLineNumber = getInputLineNumber (); int matchLevel = 1; int c = '\0'; while (matchLevel > 0) { c = nextChar (); if (c == begin) ++matchLevel; else if (c == end) --matchLevel; else if (c == '\n') break; } if (c == EOF) verbose ("%s: failed to find match for '%c' at line %lu\n", getInputFileName (), begin, inputLineNumber); } static void findMakeTags (void) { vString *name = vStringNew (); boolean newline = TRUE; boolean in_define = FALSE; boolean in_rule = FALSE; boolean variable_possible = TRUE; int c; while ((c = nextChar ()) != EOF) { if (newline) { if (in_rule) { if (c == '\t') { skipLine (); /* skip rule */ continue; } else in_rule = FALSE; } variable_possible = (boolean)(!in_rule); newline = FALSE; } if (c == '\n') newline = TRUE; else if (isspace (c)) continue; else if (c == '#') skipLine (); else if (c == '(') skipToMatch ("()"); else if (c == '{') skipToMatch ("{}"); else if (c == ':') { variable_possible = TRUE; in_rule = TRUE; } else if (variable_possible && isIdentifier (c)) { readIdentifier (c, name); if (strncmp (vStringValue (name), "endef", 5) == 0) in_define = FALSE; else if (in_define) skipLine (); else if (strncmp (vStringValue (name), "define", 6) == 0 && isIdentifier (c)) { in_define = TRUE; c = skipToNonWhite (); newMacroFromDefine (name); skipLine (); } else { c = skipToNonWhite (); if (strchr (":?+", c) != NULL) { boolean append = (boolean)(c == '+'); boolean was_colon = (c == ':'); c = nextChar (); if (was_colon) { if (c == '=') { newMacro (name); in_rule = FALSE; skipLine (); } else { in_rule = TRUE; newTarget (name); } } else if (append) { skipLine (); continue; } else { fileUngetc (c); } } else if (c == '=') { newMacro (name); in_rule = FALSE; skipLine (); } else { fileUngetc (c); } } } else variable_possible = FALSE; } vStringDelete (name); } extern parserDefinition* MakefileParser (void) { static const char *const patterns [] = { "[Mm]akefile", NULL }; static const char *const extensions [] = { "mak", "mk", NULL }; parserDefinition* const def = parserNew ("Make"); def->kinds = MakeKinds; def->kindCount = KIND_COUNT (MakeKinds); def->patterns = patterns; def->extensions = extensions; def->parser = findMakeTags; return def; } /* vi:set tabstop=4 shiftwidth=4: */