geany/tagmanager/make.c

287 lines
5.3 KiB
C
Raw Normal View History

/*
* $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 <stdio.h>
#include <string.h>
#include <ctype.h>
#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 */
char *name_val = strchr (vStringValue (name), ' ');
if (name_val != NULL) {
vStringCopyS (name, name_val + 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: */