319 lines
7.4 KiB
C
319 lines
7.4 KiB
C
|
/*
|
||
|
* Prototype search and parsing functions
|
||
|
*
|
||
|
* Copyright 2000 Jon Griffiths
|
||
|
*/
|
||
|
#include "specmaker.h"
|
||
|
|
||
|
static char *grep_buff = NULL;
|
||
|
static char *fgrep_buff = NULL;
|
||
|
|
||
|
static int symbol_from_prototype (parsed_symbol *sym, const char *prototype);
|
||
|
static const char *get_type (parsed_symbol *sym, const char *proto, int arg);
|
||
|
|
||
|
|
||
|
/*******************************************************************
|
||
|
* symbol_search
|
||
|
*
|
||
|
* Call Patrik Stridvall's 'function_grep.pl' script to retrieve a
|
||
|
* function prototype from include file(s)
|
||
|
*/
|
||
|
int symbol_search (parsed_symbol *sym)
|
||
|
{
|
||
|
static const size_t MAX_RESULT_LEN = 1024;
|
||
|
FILE *grep;
|
||
|
int attempt = 0;
|
||
|
|
||
|
assert (globals.do_code);
|
||
|
assert (globals.directory);
|
||
|
assert (sym && sym->symbol);
|
||
|
|
||
|
if (!symbol_is_valid_c (sym))
|
||
|
return - 1;
|
||
|
|
||
|
if (!grep_buff)
|
||
|
grep_buff = (char *) malloc (MAX_RESULT_LEN);
|
||
|
|
||
|
if (!fgrep_buff)
|
||
|
fgrep_buff = (char *) malloc (MAX_RESULT_LEN);
|
||
|
|
||
|
if (!grep_buff || !fgrep_buff)
|
||
|
fatal ("Out of Memory");
|
||
|
|
||
|
/* Use 'grep' to tell us which possible files the function is in,
|
||
|
* then use 'function_grep.pl' to get the prototype. If this fails the
|
||
|
* first time then give grep a more general query (that doesn't
|
||
|
* require an opening argument brace on the line with the function name).
|
||
|
*/
|
||
|
while (attempt < 2)
|
||
|
{
|
||
|
FILE *f_grep;
|
||
|
char *cmd = str_create (4, "grep -d recurse -l \"", sym->symbol,
|
||
|
!attempt ? "[:blank:]*(\" " : "\" ", globals.directory);
|
||
|
|
||
|
if (VERBOSE)
|
||
|
puts (cmd);
|
||
|
|
||
|
fflush (NULL); /* See 'man popen' */
|
||
|
|
||
|
if (!(grep = popen (cmd, "r")))
|
||
|
fatal ("Cannot execute grep -l");
|
||
|
free (cmd);
|
||
|
|
||
|
while (fgets (grep_buff, MAX_RESULT_LEN, grep))
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
|
||
|
;
|
||
|
grep_buff[i] = '\0';
|
||
|
|
||
|
if (VERBOSE)
|
||
|
puts (grep_buff);
|
||
|
|
||
|
cmd = str_create (5, "function_grep.pl ", sym->symbol,
|
||
|
" \"", grep_buff, "\"");
|
||
|
|
||
|
if (VERBOSE)
|
||
|
puts (cmd);
|
||
|
|
||
|
fflush (NULL); /* See 'man popen' */
|
||
|
|
||
|
if (!(f_grep = popen (cmd, "r")))
|
||
|
fatal ("Cannot execute function_grep.pl");
|
||
|
free (cmd);
|
||
|
|
||
|
while (fgets (grep_buff, MAX_RESULT_LEN, f_grep))
|
||
|
{
|
||
|
char *iter = grep_buff;
|
||
|
|
||
|
/* Keep only the first line */
|
||
|
symbol_clean_string(grep_buff);
|
||
|
|
||
|
for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
|
||
|
;
|
||
|
grep_buff[i] = '\0';
|
||
|
|
||
|
if (VERBOSE)
|
||
|
puts (grep_buff);
|
||
|
|
||
|
while ((iter = strstr (iter, sym->symbol)))
|
||
|
{
|
||
|
if (iter > grep_buff && iter[-1] == ' ' &&
|
||
|
(iter[strlen (sym->symbol)] == ' ' ||
|
||
|
iter[strlen (sym->symbol)] == '('))
|
||
|
{
|
||
|
if (VERBOSE)
|
||
|
puts ("Prototype looks OK, processing");
|
||
|
|
||
|
if (!symbol_from_prototype (sym, grep_buff))
|
||
|
{
|
||
|
pclose (f_grep);
|
||
|
pclose (grep);
|
||
|
return 0; /* OK */
|
||
|
}
|
||
|
if (VERBOSE)
|
||
|
puts ("Failed, trying next");
|
||
|
}
|
||
|
else
|
||
|
iter += strlen (sym->symbol);
|
||
|
}
|
||
|
}
|
||
|
pclose (f_grep);
|
||
|
}
|
||
|
pclose (grep);
|
||
|
attempt++;
|
||
|
}
|
||
|
|
||
|
return -1; /* Not found */
|
||
|
}
|
||
|
|
||
|
|
||
|
/*******************************************************************
|
||
|
* symbol_from_prototype
|
||
|
*
|
||
|
* Convert a C prototype into a symbol
|
||
|
*/
|
||
|
static int symbol_from_prototype (parsed_symbol *sym, const char *proto)
|
||
|
{
|
||
|
char *iter;
|
||
|
int found;
|
||
|
|
||
|
proto = get_type (sym, proto, -1); /* Get return type */
|
||
|
if (!proto)
|
||
|
return -1;
|
||
|
|
||
|
iter = (char *)str_match (proto, sym->symbol, &found);
|
||
|
|
||
|
if (!found)
|
||
|
{
|
||
|
/* Calling Convention */
|
||
|
iter = strchr (iter, ' ');
|
||
|
if (!iter)
|
||
|
return -1;
|
||
|
|
||
|
sym->calling_convention = str_substring (proto, iter);
|
||
|
|
||
|
iter = (char *)str_match (iter, sym->symbol, &found);
|
||
|
|
||
|
if (!found)
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
sym->calling_convention = strdup (CALLING_CONVENTION);
|
||
|
|
||
|
sym->function_name = strdup (sym->symbol);
|
||
|
proto = iter;
|
||
|
|
||
|
/* Now should be the arguments */
|
||
|
if (*proto++ != '(')
|
||
|
return -1;
|
||
|
|
||
|
for (; *proto == ' '; proto++);
|
||
|
|
||
|
if (!strncmp (proto, "void", 4))
|
||
|
return 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
/* Process next argument */
|
||
|
str_match (proto, "...", &sym->varargs);
|
||
|
if (sym->varargs)
|
||
|
return 0;
|
||
|
|
||
|
if (!(proto = get_type (sym, proto, sym->argc)))
|
||
|
return -1;
|
||
|
|
||
|
sym->argc++;
|
||
|
|
||
|
if (*proto == ',')
|
||
|
proto++;
|
||
|
else if (*proto != ')')
|
||
|
return -1;
|
||
|
|
||
|
} while (*proto != ')');
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*******************************************************************
|
||
|
* get_type
|
||
|
*
|
||
|
* Read a type from a prototype
|
||
|
*/
|
||
|
static const char *get_type (parsed_symbol *sym, const char *proto, int arg)
|
||
|
{
|
||
|
int is_const, is_volatile, is_struct, is_signed, is_unsigned, ptrs = 0;
|
||
|
char *iter, *type_str, *base_type, *catch_unsigned, dest_type;
|
||
|
|
||
|
assert (sym && sym->symbol);
|
||
|
assert (proto && *proto);
|
||
|
assert (arg < 0 || (unsigned)arg == sym->argc);
|
||
|
|
||
|
type_str = (char *)proto;
|
||
|
|
||
|
proto = str_match (proto, "const", &is_const);
|
||
|
proto = str_match (proto, "volatile", &is_volatile);
|
||
|
proto = str_match (proto, "struct", &is_struct);
|
||
|
if (!is_struct)
|
||
|
proto = str_match (proto, "union", &is_struct);
|
||
|
|
||
|
catch_unsigned = (char *)proto;
|
||
|
|
||
|
proto = str_match (proto, "unsigned", &is_unsigned);
|
||
|
proto = str_match (proto, "signed", &is_signed);
|
||
|
|
||
|
/* Can have 'unsigned const' or 'const unsigned' etc */
|
||
|
if (!is_const)
|
||
|
proto = str_match (proto, "const", &is_const);
|
||
|
if (!is_volatile)
|
||
|
proto = str_match (proto, "volatile", &is_volatile);
|
||
|
|
||
|
base_type = (char *)proto;
|
||
|
iter = (char *)str_find_set (proto, " ,*)");
|
||
|
if (!iter)
|
||
|
return NULL;
|
||
|
|
||
|
if (arg < 0 && (is_signed || is_unsigned))
|
||
|
{
|
||
|
/* Prevent calling convention from being swallowed by 'un/signed' alone */
|
||
|
if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
|
||
|
strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
|
||
|
{
|
||
|
iter = (char *)proto;
|
||
|
base_type = catch_unsigned;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
catch_unsigned = NULL;
|
||
|
|
||
|
/* FIXME: skip const/volatile here too */
|
||
|
for (proto = iter; *proto; proto++)
|
||
|
if (*proto == '*')
|
||
|
ptrs++;
|
||
|
else if (*proto != ' ')
|
||
|
break;
|
||
|
|
||
|
if (!*proto)
|
||
|
return NULL;
|
||
|
|
||
|
type_str = str_substring (type_str, proto);
|
||
|
if (iter == base_type || catch_unsigned)
|
||
|
{
|
||
|
/* 'unsigned' with no type */
|
||
|
char *tmp = str_create (2, type_str, " int");
|
||
|
free (type_str);
|
||
|
type_str = tmp;
|
||
|
}
|
||
|
symbol_clean_string (type_str);
|
||
|
|
||
|
dest_type = symbol_get_type (type_str);
|
||
|
|
||
|
if (arg < 0)
|
||
|
{
|
||
|
sym->return_text = type_str;
|
||
|
sym->return_type = dest_type;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sym->arg_type [arg] = dest_type;
|
||
|
sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;
|
||
|
|
||
|
if (*proto == ',' || *proto == ')')
|
||
|
sym->arg_name [arg] = str_create_num (1, arg, "arg");
|
||
|
else
|
||
|
{
|
||
|
iter = (char *)str_find_set (proto, " ,)");
|
||
|
if (!iter)
|
||
|
{
|
||
|
free (type_str);
|
||
|
return NULL;
|
||
|
}
|
||
|
sym->arg_name [arg] = str_substring (proto, iter);
|
||
|
proto = iter;
|
||
|
}
|
||
|
sym->arg_text [arg] = type_str;
|
||
|
|
||
|
}
|
||
|
return proto;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef __GNUC__
|
||
|
/*******************************************************************
|
||
|
* search_cleanup
|
||
|
*
|
||
|
* Free memory used while searching (a niceity)
|
||
|
*/
|
||
|
void search_cleanup (void) __attribute__ ((destructor));
|
||
|
void search_cleanup (void)
|
||
|
{
|
||
|
if (grep_buff)
|
||
|
free (grep_buff);
|
||
|
|
||
|
if (fgrep_buff)
|
||
|
free (fgrep_buff);
|
||
|
}
|
||
|
#endif
|
||
|
|