/* $Id: semantic.c,v 2.0.1.14 1996/03/21 08:23:48 greyham Exp greyham $
*
* C manual page generator
* These routines implement the semantic actions executed by the yacc parser.
*/
#include "c2man.h"
#include <ctype.h>
#include <errno.h>
#include "semantic.h"
#include "enum.h"
#include "manpage.h"
#include "strconcat.h"
#include "output.h"
/* Return TRUE if the given identifier is really a typedef name.
* Search the symbol table for the identifier.
*/
boolean
is_typedef_name (name)
char *name;
{
return (boolean)(find_symbol(typedef_names, name) != NULL);
}
/* Initialize a new declaration specifier part.
*/
void
new_decl_spec (decl_spec, text, flags)
DeclSpec *decl_spec;
char *text;
int flags;
{
decl_spec->text = text ? strduplicate(text) : NULL;
decl_spec->flags = flags;
decl_spec->enum_list = NULL;
}
/* Free storage used by a declaration specifier part.
*/
void
free_decl_spec (decl_spec)
DeclSpec *decl_spec;
{
safe_free(decl_spec->text); /* could be an ellipsis you know */
}
/* Initialize a new declaration specifier part, including an enum part.
*/
void
new_enum_decl_spec (decl_spec, text, flags, enum_list)
DeclSpec *decl_spec;
char *text;
int flags;
EnumeratorList *enum_list;
{
decl_spec->text = text;
decl_spec->flags = flags;
decl_spec->enum_list = enum_list;
}
/* Initialize a new declaration specifier part, but don't strdup the text. */
void
dyn_decl_spec (decl_spec, text, flags)
DeclSpec *decl_spec;
char *text;
unsigned int flags;
{
decl_spec->text = text;
decl_spec->flags = flags;
decl_spec->enum_list = NULL;
}
/* Append two declaration specifier parts together.
*/
void
join_decl_specs (result, a, b)
DeclSpec *result, *a, *b;
{
if (a->text)
{
if (b->text)
{
result->text = strconcat(a->text, " ", b->text, NULLCP);
free(a->text);
free(b->text);
}
else
result->text = a->text;
}
else
result->text = b->text;
result->flags = a->flags | b->flags;
/* only one of the decl specs should have an enum list! */
result->enum_list = a->enum_list ? a->enum_list : b->enum_list;
}
/* Allocate and initialize a declarator. */
Declarator *
new_declarator (text, name)
char *name, *text;
{
Declarator *d;
d = (Declarator *)safe_malloc(sizeof(Declarator));
d->text = text;
d->name = name;
d->type = DECL_SIMPLE;
d->comment = NULL;
d->retcomment = NULL;
new_ident_list(&d->params);
d->head = d;
d->func_stack = NULL;
return d;
}
/* Free storage used by a declarator.
*/
void
free_declarator (d)
Declarator *d;
{
#ifdef DEBUG
fprintf(stderr,"free_declarator: decl = %lx, name = %s, text = %s\n",
(long)d, d->name?d->name:"NULL", d->text?d->text:"NULL");
#endif
safe_free(d->name); /* could be an ellipsis (ie: no name) */
safe_free(d->text); /* ellipsis is marked by no text too */
safe_free(d->comment);
safe_free(d->retcomment);
free_param_list(&(d->params));
if (d->func_stack != NULL)
free_declarator(d->func_stack);
free(d);
}
/* add a comment to the last declarator in the list */
int
comment_last_decl(list, comment)
DeclaratorList *list;
char *comment;
{
if (list->last->comment)
{
declarator_error(list->last);
free(comment);
return 0;
}
else
list->last->comment = comment;
return 1;
}
/* Initialize a declarator list and add the given declarator to it.
*/
void
new_decl_list (decl_list, declarator)
DeclaratorList *decl_list;
Declarator *declarator;
{
decl_list->first = decl_list->last = declarator;
declarator->next = NULL;
}
/* Free storage used by the declarators in the declarator list.
*/
void
free_decl_list (decl_list)
DeclaratorList *decl_list;
{
Declarator *d, *next;
#ifdef DEBUG
fprintf(stderr,"free_decl_list: decl_list = %lx, first = %lx\n",
(long)decl_list, (long)decl_list->first);
#endif
d = decl_list->first;
while (d != NULL) {
next = d->next;
free_declarator(d);
d = next;
}
}
/* Add the declarator to the declarator list.
*/
void
add_decl_list (to, from, declarator)
DeclaratorList *to, *from;
Declarator *declarator;
{
to->first = from->first;
from->last->next = declarator;
to->last = declarator;
to->last->next = NULL;
}
/* Initialize the parameter structure.
*/
void
new_parameter (param, decl_spec, declarator, comment_before, comment_after)
Parameter *param; /* pointer to structure to be initialized */
DeclSpec *decl_spec; /* declaration specifier structure */
Declarator *declarator; /* declarator structure */
char *comment_before; /* comment before the param */
char *comment_after; /* comment after the param */
{
if (decl_spec == NULL) {
new_decl_spec(&(param->decl_spec), NULLCP, DS_JUNK);
} else {
param->decl_spec = *decl_spec;
}
if (declarator == NULL) {
declarator = new_declarator(NULLCP, NULLCP);
}
param->declarator = declarator;
if (comment_before && comment_after)
{
parameter_error(param);
free(comment_after); /* comment_before will go in Parameter */
}
param->declarator->comment =
comment_before ? comment_before : comment_after;
param->suppress = FALSE;
param->duplicate = FALSE;
}
/* Free the storage used by the parameter.
*/
void
free_parameter (param)
Parameter *param;
{
free_decl_spec(&(param->decl_spec));
free_declarator(param->declarator);
}
/* add a comment to the last parameter in the list */
int
comment_last_parameter(list, comment)
ParameterList *list;
char *comment;
{
if (list->last == NULL)
{
yyerror("comment '%s' applies to non-existent parameter", comment);
free(comment);
return 0;
}
if (list->last->declarator->comment)
{
parameter_error(list->last);
free(comment);
return 0;
}
else
list->last->declarator->comment = comment;
return 1;
}
/* Initialize a list of function parameters.
*/
void
new_param_list (param_list, param)
ParameterList *param_list;
Parameter *param;
{
Parameter *p;
p = (Parameter *)safe_malloc((unsigned)sizeof(Parameter));
*p = *param;
param_list->first = param_list->last = p;
p->next = NULL;
}
/* Free storage used by the elements in the function parameter list.
*/
void
free_param_list (param_list)
ParameterList *param_list;
{
Parameter *p, *next;
p = param_list->first;
while (p != NULL) {
next = p->next;
free_parameter(p);
free(p);
p = next;
}
}
/* Add the function parameter declaration to the list.
*/
void
add_param_list (to, from, param)
ParameterList *to, *from;
Parameter *param;
{
Parameter *p;
p = (Parameter *)safe_malloc((unsigned)sizeof(Parameter));
*p = *param;
to->first = from->first;
from->last->next = p;
to->last = p;
p->next = NULL;
}
/* Initialize an empty list of function parameter names.
*/
void
new_ident_list (param_list)
ParameterList *param_list;
{
param_list->first = param_list->last = NULL;
}
/* Add an item to the list of function parameter declarations but set only
* the parameter name field and the comments.
*/
void
add_ident_list (to, from, ident)
ParameterList *to, *from;
Identifier *ident;
{
Parameter *p;
Declarator *declarator;
p = (Parameter *)safe_malloc(sizeof(Parameter));
declarator = new_declarator(ident->name, strduplicate(ident->name));
new_parameter(p, (DeclSpec *)NULL, declarator, ident->comment_before,
ident->comment_after);
to->first = from->first;
if (to->first == NULL) {
to->first = p;
} else {
from->last->next = p;
}
to->last = p;
p->next = NULL;
}
/* Search the list of parameters for a matching parameter name.
* Return a pointer to the matching parameter or NULL if not found.
*/
static Parameter *
search_parameter_list (params, name)
ParameterList *params;
char *name;
{
Parameter *p;
for (p = params->first; p != NULL; p = p->next) {
if (p->declarator->name && strcmp(p->declarator->name, name) == 0)
return p;
}
return (Parameter *)NULL;
}
/* This routine is called to generate function prototypes from traditional
* style function definitions. For each parameter name in the declarator
* list, find the matching parameter name in the parameter list and set
* that parameter's declaration specifier.
* This is also where we promote formal parameters. Parameters of type
* "char", "unsigned char", "short", or "unsigned short" get promoted to
* "int". Parameters of type "float" are promoted to "double".
*/
void
set_param_types (params, decl_spec, declarators, comment, eolcomment)
ParameterList *params;
DeclSpec *decl_spec;
DeclaratorList *declarators;
char *comment;
char *eolcomment;
{
Declarator *d;
Parameter *p;
if (comment && eolcomment)
{
yyerror("parameter declaration has multiple comments");
return;
}
if (!comment) comment = eolcomment;
for (d = declarators->first; d != NULL; d = d->next) {
/* Search the parameter list for a matching name. */
if ((p = search_parameter_list(params, d->name)) == NULL) {
output_error();
fprintf(stderr, "declared argument \"%s\" is missing\n", d->name);
} else {
char *decl_spec_text = decl_spec->text;
if (promote_param && strcmp(d->text, d->name) == 0) {
if (decl_spec->flags & (DS_CHAR | DS_SHORT))
decl_spec_text = "int";
else if (decl_spec->flags & DS_FLOAT)
decl_spec_text = "double";
}
safe_free(p->decl_spec.text); /* there shouldn't be one, but...*/
p->decl_spec.text = strduplicate(decl_spec_text);
if (p->decl_spec.flags != decl_spec->flags)
{
if (p->decl_spec.flags & DS_JUNK)
p->decl_spec.flags = decl_spec->flags;
else
parameter_error(p);
}
if (p->decl_spec.enum_list != decl_spec->enum_list)
{
if (p->decl_spec.enum_list == NULL)
p->decl_spec.enum_list = decl_spec->enum_list;
else
parameter_error(p);
}
free_declarator(p->declarator);
p->declarator = d;
if (comment)
{
if (p->declarator->comment)
parameter_error(p);
else
p->declarator->comment = strduplicate(comment);
}
}
}
free_decl_spec(decl_spec);
safe_free(comment);
}
/* Output a declaration specifier for an external declaration.
*/
void
output_decl_spec (decl_spec)
DeclSpec *decl_spec;
{
output->text(decl_spec->text);
}
static void
output_parameters _((Declarator *d, boolean format));
/* does a function have any parameters?
* This accounts for both fn() and fn(void)
*/
boolean has_parameters(d)
const Declarator *d;
{
Parameter *first = d->head->params.first;
return (first != NULL &&
(first->declarator->text != NULL ||
strcmp(first->decl_spec.text, "void")));
}
/* output a declarator name, stripping leading underscores if necessary */
void output_decl_text(text, keep_underscores)
char *text;
boolean keep_underscores;
{
if (!keep_underscores)
{
/* skip leading stuff before the actual name */
while (*text && *text != '_' && !isalnum(*text))
output->character(*text++);
while (text[0] == '_' && text[1]) text++;
}
output->text(text);
}
/* Output a function declarator.
*/
static void
output_func_declarator (declarator, format)
Declarator *declarator;
boolean format;
{
char *s, *t, *decl_text;
/* Output declarator text before function declarator place holder. */
if ((s = strstr(declarator->text, "%s")) == NULL)
return;
*s = '\0';
output->text(declarator->text);
/* Substitute place holder with function declarator. */
if (!is_function_declarator(declarator->func_stack)) {
decl_text = declarator->func_stack->text;
if (declarator->name == NULL || declarator->name[0] == '\0') {
output->text(decl_text);
} else {
/* Output the declarator text before the declarator name. */
if ((t = strstr(decl_text, declarator->name)) == NULL)
return;
*t = '\0';
output->text(decl_text);
*t = declarator->name[0];
if (format && strcmp(declarator_prefix, " ") != 0)
output_format_string(declarator_prefix);
/* Output the declarator name. */
output_decl_text(declarator->name, format);
/* Output the remaining declarator text. */
output->text(t + strlen(declarator->name));
/* Output the declarator suffix. */
if (format) output_format_string(declarator_suffix);
}
} else {
output_func_declarator(declarator->func_stack,format);
}
*s = '%';
s += 2;
/* Output declarator text up to but before parameters place holder. */
if ((t = strstr(s, "()")) == NULL)
return;
*t = '\0';
output->text(s);
/* Substitute place holder with function parameters. */
output->character(*t++ = '(');
output_parameters(declarator, format);
output->text(t);
}
/* Output a declarator.
*/
void
output_declarator (d, format)
Declarator *d;
boolean format;
{
if (d->func_stack) {
output_func_declarator(d, format);
} else {
output_decl_text(d->text, format);
}
}
/* Output a function parameter.
*/
void
output_parameter (p)
Parameter *p;
{
if (p->decl_spec.text)
output->text(p->decl_spec.text);
else
/* Check for parameter names with no declaration specifiers. This
* happens when a parameter name appears in the identifier list of a
* function definition but does not appear in the parameter declaration
* part. The default type in this cause is "int".
*/
if (p->declarator->text && strcmp(p->declarator->text, "...") != 0)
output->text("int ");
/* not all parameters must have declarators: might be a prototype */
if (p->declarator->text) {
if (p->decl_spec.text)
output->character(' ');
/* don't format parameters; keep it all on one line */
output_declarator(p->declarator, FALSE);
}
}
/* Output the list of function parameters.
*/
static void
output_parameters (d, format)
Declarator *d;
boolean format;
{
if (has_parameters(d)) {
Parameter *p = d->params.first;
if (format) output_format_string(first_param_prefix);
output_parameter(p);
p = p->next;
while (p != NULL) {
output->character(',');
if (format) output_format_string(middle_param_prefix);
output_parameter(p);
p = p->next;
}
if (format) output_format_string(last_param_suffix);
}
else
output->text("void");
}
/* remember variable and function declarations. */
int
remember_declarations (comment, decl_spec, decl_list, eolcomment)
char *comment; /* comment before */
DeclSpec *decl_spec; /* declaration specifier */
DeclaratorList *decl_list; /* list of declared variables */
char *eolcomment; /* eol comment after */
{
Declarator *d, *next;
int ret = 1;
/* attach EOL comment to last one in list */
if (eolcomment)
{
Declarator *attach;
/* if it's a function, attach it to the last parameter */
if (is_function_declarator(decl_list->last) &&
decl_list->last->head->params.last)
attach = decl_list->last->head->params.last->declarator;
else
attach = decl_list->last;
if (attach->comment)
{
declarator_error(attach);
free(eolcomment);
ret = 0;
}
else
attach->comment = eolcomment;
}
/* special case of a single declarator handled efficiently */
if (decl_list->first && decl_list->first->next == NULL)
{
d = decl_list->first;
/* free the declarator comment if it isn't going to get used */
if (comment)
safe_free(d->comment);
else
comment = d->comment;
/* and nuke it from the declarator so free_declarator won't free it
* (since safe_free will do that) if new_manual_page decides to throw
* it away.
*/
d->comment = NULL;
new_manual_page(comment, decl_spec, d);
}
else
{
for (d = decl_list->first; d != NULL; d = next)
{
DeclSpec spec_copy;
char *comment_copy;
next = d->next;
#ifdef DEBUG
fprintf(stderr,
"remember_declarations: text=%s name=%s\ncomment: %s\n",
d->text,d->name, comment ? comment : "NULL");
#endif
spec_copy = *decl_spec;
spec_copy.text = strduplicate(decl_spec->text);
comment_copy = d->comment ? d->comment :
(comment ? strduplicate(comment) : NULL);
d->comment = NULL;
new_manual_page(comment_copy, &spec_copy,d);
}
/* free 'em up */
free_decl_spec(decl_spec);
safe_free(comment);
}
return ret;
}
void parameter_error(param)
Parameter *param;
{
yyerror("parameter '%s' has multiple comments", param->declarator->name);
}
void declarator_error(decl)
Declarator *decl;
{
yyerror("declarator '%s' has multiple comments", decl->name);
}
/* is a declarator for a function? (as opposed to a variable) */
boolean is_function_declarator(decl)
const Declarator *decl;
{
return decl->type == DECL_FUNCTION || decl->type == DECL_FUNCDEF;
}
/* is a comment a start of a numbered list item */
boolean is_numbered(text)
const char *text;
{
char *next = NULL;
if (*text == '(') {
++text;
errno = 0;
strtol(text, &next, 0);
if (errno)
return FALSE;
if (*next == ')')
return TRUE;
}
else {
errno = 0;
strtol(text, &next, 0);
if (errno)
return FALSE;
if (*next == '.' || *next == ')')
return TRUE;
}
return FALSE ;
}
syntax highlighted by Code2HTML, v. 0.9.1