// clang-format off /* * $Id: make.c 751 2010-02-27 17:41:57Z elliotth $ * * 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 "third_party/ctags/general.h" /* must always come first */ #include "libc/mem/alg.h" #include "libc/str/str.h" #include "libc/str/str.h" #include "third_party/ctags/options.h" #include "third_party/ctags/parse.h" #include "third_party/ctags/read.h" #include "third_party/ctags/vstring.h" /* * DATA DEFINITIONS */ typedef enum { K_MACRO } shKind; static kindOption MakeKinds [] = { { TRUE, 'm', "macro", "macros"} }; /* * 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 void readIdentifier (const int first, vString *const id) { int c = first; vStringClear (id); while (isIdentifier (c)) { vStringPut (id, c); c = nextChar (); } 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' || c == EOF) 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 (strcmp (vStringValue (name), "endef") == 0) in_define = FALSE; else if (in_define) skipLine (); else if (strcmp (vStringValue (name), "define") == 0 && isIdentifier (c)) { in_define = TRUE; c = skipToNonWhite (); readIdentifier (c, name); makeSimpleTag (name, MakeKinds, K_MACRO); skipLine (); } else { if (strcmp(vStringValue (name), "export") == 0 && isIdentifier (c)) { c = skipToNonWhite (); readIdentifier (c, name); } c = skipToNonWhite (); if (strchr (":?+", c) != NULL) { boolean append = (boolean)(c == '+'); if (c == ':') in_rule = TRUE; c = nextChar (); if (c != '=') fileUngetc (c); else if (append) { skipLine (); continue; } } if (c == '=') { makeSimpleTag (name, MakeKinds, K_MACRO); in_rule = FALSE; skipLine (); } } } else variable_possible = FALSE; } vStringDelete (name); } extern parserDefinition* MakefileParser (void) { static const char *const patterns [] = { "[Mm]akefile", "GNUmakefile", 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: */