Logo Search packages:      
Sourcecode: c2man version File versions  Download package

semantic.c

/* $Id: semantic.c,v 2.0.1.14 1996/03/21 08:23:48 greyham Exp $
 *
 * 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 ;
}


Generated by  Doxygen 1.6.0   Back to index