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

manpage.c

/* $Id: manpage.c,v 2.0.1.55 1996/03/21 08:24:21 greyham Exp $
 * stuff to do with manual page outputing
 */

#include "c2man.h"

#include <errno.h>
#include <ctype.h>

#include "manpage.h"
#include "strconcat.h"
#include "strappend.h"
#include "semantic.h"
#include "output.h"

#ifdef I_SYS_FILE
#include <sys/file.h>
#endif

/* list of manual pages */
ManualPage *firstpage = NULL;
ManualPage **lastpagenext = &firstpage;

void dummy() {}

void
new_manual_page(comment, decl_spec, declarator)
     char *comment;
     DeclSpec *decl_spec;
     Declarator *declarator;
{
    ManualPage *newpage;

    /* check that we really want a man page for this */
    if ((!comment) ||
      !inbasefile ||
      (!variables_out && !is_function_declarator(declarator)) ||
      (decl_spec->flags & DS_JUNK) ||
      (!static_out && (decl_spec->flags & DS_STATIC) && !header_file) ||

      /* only document extern stuff if it's in a header file, or includes a
       * function definition.
       */
      ((decl_spec->flags & DS_EXTERN) && !header_file &&
                            declarator->type != DECL_FUNCDEF))
    {
      free_decl_spec(decl_spec);
      free_declarator(declarator);
      safe_free(comment);
      return;
    }
    
    declarator->comment = comment;
    
    newpage = (ManualPage *)safe_malloc(sizeof *newpage);
    newpage->decl_spec = (DeclSpec *)safe_malloc(sizeof *newpage->decl_spec);
    newpage->declarator = declarator;

    *newpage->decl_spec = *decl_spec;
    newpage->sourcefile = strduplicate(basefile);
    newpage->sourcetime = basetime;

    *lastpagenext = newpage;
    newpage->next = NULL;
    lastpagenext = &newpage->next;
}

void free_manual_page(page)
     ManualPage *page;
{
    free_decl_spec(page->decl_spec);
    free(page->decl_spec);
    free_declarator(page->declarator);
    safe_free(page->sourcefile);
}

/* free the list of manual pages */
void free_manual_pages(first)
     ManualPage *first;
{
    ManualPage *page, *next;

    /* free any terse description read from the file */
    if (group_terse && !terse_specified)
    {
      free(group_terse);
      group_terse = NULL;
    }
    
    for (page = first;page;page = next)
    {
      next = page->next;
      free_manual_page(page);
      free(page);
    }
}

/* allocate a substring starting at start, ending at end (NOT including *end) */
char *alloc_string(start, end)
     const char *start;
     const char *end;
{
    int len = end - start;
    char *ret;
    if (len == 0) return NULL;
    
    ret = (char *)safe_malloc((size_t)len+1);

    strncpy(ret,start,len);
    ret[len] = '\0';

    return ret; 
}

/* remember the terse description from the first comment in a file */
void remember_terse(comment)
     char *comment;
{
    char *c, *d;
    
    enum { STUFF, LEADSPACE, DASH, TRAILSPACE, FOUND } state = STUFF;

    /* if we've found a terse comment in a previous file, or one was
     * specified on the command line, forget it.
     */
    if (group_terse)    return;

    /* look for a whitespace surrounded sequence of dashes to skip */
    for (c = comment;*c && state != FOUND;c++)
    {
      switch (state)
      {
      case STUFF: if (isspace(*c))  state = LEADSPACE;
                  break;
      case LEADSPACE:   if (*c == '-')          state = DASH;
                  else if (!isspace(*c))  state = STUFF;
                  break;
      case DASH:  if (isspace(*c))  state = TRAILSPACE;
                  else if (*c != '-')     state = STUFF;
                  break;
      case TRAILSPACE:if (!isspace(*c))   { c--; state = FOUND; }
                  break;
      case FOUND: break;
      }
    }

    /* if no dashes were found, go back to the start */
    if (state != FOUND) c = comment;

    d = c + 1;
    
    while (*d && *d != '\n')
      d++;

    group_terse = alloc_string(c,d);
}

/* output a comment in man page form, followed by a newline */
void
output_comment(comment)
const char *comment;
{
    if (!comment || !comment[0])
      output->text("Not Documented.");
    else if (fixup_comments)
      output->description(comment);
    else
      output->text(comment);

    output->character('\n');
}

/* output the phrase "a[n] <type name>" */
void output_conjunction(text)
char *text;
{
    output->character('a');
    if (strchr("aAeEiIoOuU",text[0]))     output->character('n');
    output->character(' ');
    output->code(text);
}

/* output the description for an identifier; be it return value or param */
static void output_identifier_description(comment, outfunc,
                                        decl_spec, declarator)
    const char *comment;            /* comment for this identifier */
    void (*outfunc) _((const char *));    /* function to output comment */
    const DeclSpec *decl_spec;
    const Declarator *declarator;
{
    /* one day, this may document the contents of structures too */

    /* output list of possible enum values, if any */
    if (decl_spec->enum_list)
    {
      int maxtaglen = 0;
      char *longestag = NULL;
      int descriptions = 0;
      int entries = 0;
      Enumerator *e;
      int is_first = 1;
      boolean started = FALSE;
      
      /* don't output the "Not Doc." message for enums */
      if (comment)
      {
          (*outfunc)(comment);
          output->blank_line();
      }
      
      /* see if any have descriptions */
      for (e = decl_spec->enum_list->first; e; e = e->next)
          if (e->name[0] != '_')
          {
            int taglen = strlen(e->name);
            if (taglen > maxtaglen)
            {
                maxtaglen = taglen;
                longestag = e->name;
            }
            if (e->comment)   descriptions = 1;
            entries++;
          }

      /* if there are a lot of them, the list may be automatically generated,
       * and probably isn't wanted in every manual page.
       */
      if (entries > 20)
      {
          char entries_s[15];
          sprintf(entries_s, "%d", entries);
          output->text("Since there are ");
          output->text(entries_s);
          output->text(" possible values for ");
          output_conjunction(decl_spec->text);
          output->text(", they are not all listed here.\n");
      }
      else if (entries > 0)   /* skip the pathological case */
      {
          /* the number of possibilities is reasonable; list them all */
          output->text("Possible values for ");
          output_conjunction(decl_spec->text);
          output->text(" are as follows:\n");
    
          for (e = decl_spec->enum_list->first; e; e = e->next)
          {
            /* don't print names with a leading underscore! */
            if (e->name[0] == '_')  continue;
            
            if (e->group_comment)
            {
                /* break out of table mode for the group comment */
                if (started)
                {
                  if (descriptions)
                      output->table_end();
                  else
                      output->list_end();
                  started = FALSE;
                }
                output->indent();
                output_comment(e->group_comment);
            }
    
            if (!started)
            {
                if (descriptions)
                  output->table_start(longestag);
                else
                  output->list_start();
                started = TRUE;
            }
    
            if (descriptions)
                output->table_entry(e->name, e->comment);
            else
            {
                if (!is_first)
                  output->list_separator();
                is_first = 0;
                output->list_entry(e->name);
            }
          }
    
          if (started)
          {
            if (descriptions)
                output->table_end();
            else
                output->list_end();
          }
      }
    } 
    else
      (*outfunc)(comment);
}

/* is there automatic documentation here? */
static boolean auto_documented(page)
const ManualPage *page;
{
    /* one day we may handle structs too */
    return
      page->decl_spec->enum_list != NULL;    /* enums are self-documenting. */
}

/* decide if a manual page needs a RETURNS section.
 * If this is true, then output_identifier_description must be able to generate
 * sensible output for it.
 */
static boolean needs_returns_section(page)
const ManualPage *page;
{
    return 
      (page->returns && page->returns[0]) ||
      (auto_documented(page) && is_function_declarator(page->declarator));
}

/* does this declarator have documented parameters? */
boolean has_documented_parameters(d)
const Declarator *d;
{
    if (has_parameters(d))
    {
      Parameter *p;

      for (p = d->head->params.first; p != NULL; p = p->next)
          if (p->declarator->comment || always_document_params)
            return TRUE;
    }
    return FALSE;
}

/* Output the list of function parameter descriptions.
 */
void
output_parameter_descriptions (params, function)
ParameterList *params;
char *function;
{
    Parameter *p;
    boolean tag_list_started = FALSE;

    for (p = params->first; p != NULL; p = p->next)
    {
      if (p->suppress ||
          (!always_document_params && p->declarator->comment == NULL))
            continue;

      if (!tag_list_started)
      {
        output->tag_list_start();
        tag_list_started = TRUE;
      }

      if (p->duplicate)
          output->tag_entry_start_extra();
      else
          output->tag_entry_start();

      output_parameter(p);

      /* include function name if it's a duplicate */
      if (p->duplicate)
          output->tag_entry_end_extra(function);
      else
          output->tag_entry_end();

      output_identifier_description(p->declarator->comment, output_comment,
                                    &p->decl_spec, p->declarator);
    }

    if (tag_list_started)
      output->tag_list_end();
}

/* split out the 'Returns:' section of a function comment */
boolean
split_returns_comment(comment, description, returns)
     char *comment;
     char **description;
     char **returns;
{
    char *retstart;

    for (retstart = comment;
       retstart;
       retstart = strchr(retstart,'\n'))
    {
      if (*retstart == '\n')  retstart++; /* skip the newline */

      if (!strncmpi(retstart, "returns",(size_t)7))
      {         
          char *descend = retstart - 2;   /* back before newline */

          /* go back to the end of the description in case there were
           * linefeeds before the returns.
           */
          while (descend > comment && isspace(*descend))
            descend--;

          *description =
            descend > comment ? alloc_string(comment,descend+1) : NULL;

          retstart += 7;
            
          while (*retstart == ':' || isspace(*retstart))
            retstart++;

          if (*retstart)
            *returns = strduplicate(retstart);
          else
            *returns = NULL;
          return TRUE;
      }
    }

    *description = comment;
    *returns = NULL;
    return FALSE;
}

/* skip to past the dash on the first line, if there is one
 * The dash must be surrounded by whitespace, so hyphens are not skipped.
 */
const char *skipdash(c)
const char *c;
{
    const char *d;

    /* ignore anything on the first line, up to a dash (if any) */
    for (d = c + 1; *d && *d != '\n' && *d != '-'; d++)
      ;

    if (isspace(d[-1]) && d[0] == '-' && isspace(d[1]))
    {
      do
          d++;
      while (*d && *d != '\n' && isspace(*d));

      if (*d && *d != '\n')   c = d;
    }
    return c;
}

/* split the function comment into manual page format.
 * returns TRUE if the DESCRIPTION field was explicit.
 */
boolean
split_function_comment(comment, identifier_name,
                            terse, description, returns, extra_sections)
    const char *comment;
    const char *identifier_name;
     char **terse;
     char **description;
     char **returns;
     Section **extra_sections;
{
    const char *c, *start_text = NULL, *end_text = NULL;
    char **put_ptr = NULL;
    Section *first_section, **lastnextsection = &first_section;
    boolean explicit_description = FALSE;
    boolean lastblank = TRUE;
    boolean skip_dash = FALSE;
    
    *description = *returns = NULL;
    if (terse)    *terse = NULL;

    /* for each line... */
    for (c = comment; *c;)
    {
      const char *start_line = c;
      boolean section_heading;
      /* remember if it's a blank line */
      if (*c == '\n')
      {
          lastblank = TRUE;
          c++;
          continue;
      }

      /* if the last one was blank, perhaps this one is a section heading
       */
      if (lastblank)
      {
          boolean need_colon = FALSE;

          /* see if we've found the start of a SECTION */
          while (isalnum(*c) || *c == ' ' || *c == '/')
          {
            if (isspace(*c)) need_colon = TRUE;
            c++;
          }
    
          section_heading = (!need_colon && *c == '\n') ||
                  (*c == ':' && (!need_colon || *(c+1) == '\n')) ||
                  (!need_colon && *c == '\0' && start_line == comment);
      }
      else
          section_heading = FALSE;

      lastblank = FALSE;      /* this one's not blank; for next time */

      if (section_heading)
      {
          size_t section_len = c - start_line; /* length of section name */

          /* yes, we've found a SECTION; store the previous one (if any) */
          if (put_ptr && start_text)
          {
            if (skip_dash)    start_text = skipdash(start_text);
            *put_ptr = alloc_string(start_text,end_text);
          }

          skip_dash = FALSE;

          /* check for comments that start with the name of the identifier */
          if (start_line == comment &&
            !strncmp(start_line, identifier_name, section_len))
          {
            put_ptr = description;
          }

          /* only accept NAME if not grouped */
          else if (terse && 
                 (!strncmpi(start_line,"NAME", section_len) ||
                  !strncmpi(start_line,"FUNCTION", section_len) ||
                  !strncmpi(start_line,"PROCEDURE", section_len) ||
                  !strncmpi(start_line,"ROUTINE", section_len))
                 )

          {
            put_ptr = terse;
            skip_dash = TRUE;
          }
          else if (!strncmpi(start_line,"DESCRIPTION", section_len))
          {
            explicit_description = TRUE;
            put_ptr = description;
          }
          else if (!strncmpi(start_line,"RETURNS", section_len))
          {
            put_ptr = returns;
          }
          else
          {
            /* allocate a new section */
            Section *new_section =
                        (Section *)safe_malloc(sizeof *new_section);

            *lastnextsection = new_section;
            lastnextsection = &new_section->next;

            new_section->name = alloc_string(start_line,c);
            strtoupper(new_section->name);
            new_section->text = NULL;
            new_section->been_output = FALSE; /* not been output yet */
            put_ptr = &new_section->text;
          }

          /* defer decision about where text starts till we find some */
          start_text = NULL;

          if (*c == ':')      /* skip the terminating : */
          {
            c++;

            /* skip forward to the start of the text */
            while (*c && *c != '\n' && isspace(*c))
                c++;

            /* if we find the text here, then we've got it */
            if (*c && *c != '\n')
                start_text = c;
          }
      }
      else
      {
          /* are we looking at the top of the function comment? */
          if (start_line == comment)
          {
            /* only look for terse comment if not grouped together */
            if (terse)
            {
                const char *endterse, *afterdash = skipdash(start_line);

                /* find the end of the terse comment */
                while (*c && *c != '.' && *c != '\n')
                  c++;

                endterse = *c == '.' ? c+1 : c;
                *terse = alloc_string(
                  afterdash < endterse ? afterdash : start_line,
                  endterse);

                /* skip it if it's a ., and any trailing spaces */
                if (*c == '.')
                  do c++; while (*c && *c != '\n' && isspace(*c));

                start_text = NULL;  /* look for it */

                if (*c && *c != '\n')
                  /* actually, it's a description, starting here */
                  start_text = c;
            }
            /* must be a description starting at the beginning of the line.
             */
            else
                start_text = start_line;

            put_ptr = description;
          }
          else
            /* have we just located the first real text in a section? */
            if (put_ptr && !start_text)   start_text = start_line;
      }

      /* skip the line */
      if (*c && *c != '\n')
          while (*c && *c != '\n')  c++;

      end_text = c;     /* so far, the text ends at the end of this line */
      if (*c)     c++;
    }

    /* store the last one */
    if (put_ptr && start_text)
    {
      if (skip_dash)    start_text = skipdash(start_text);
      *put_ptr = alloc_string(start_text,end_text);
    }

    /* terminate (or nuke) section list */
    *lastnextsection = NULL;

    *extra_sections = first_section;

    return explicit_description;
}

/* see if two parameters are declared identically */
boolean params_identical(first, second)
     Parameter *first;
     Parameter *second;
{
    return
      first->decl_spec.flags == second->decl_spec.flags &&

      /* there may be no decl_spec.text if it's an ellipsis arg */
      ((!first->decl_spec.text && !second->decl_spec.text) ||
       (first->decl_spec.text && second->decl_spec.text &&
        !strcmp(first->decl_spec.text, second->decl_spec.text))) &&

      ((!first->declarator->text && !second->declarator->text) ||
       (first->declarator->text && second->declarator->text &&
        !strcmp(first->declarator->text, second->declarator->text)));
}

/* search all the parameters in this grouped manual page for redundancies */
boolean mark_duplicate_parameters(firstpage)
     ManualPage *firstpage;
{
    Parameter *param;
    boolean any = FALSE;
    ManualPage *page;

    for (page = firstpage; page; page = page->next)
    {
      if (has_parameters(page->declarator))
      for (param = page->declarator->head->params.first; param;
                                          param = param->next)
      {
          ManualPage *otherpage;
          Parameter *otherparam;
          
          if (always_document_params || param->declarator->comment)
            any = TRUE;

          for (otherpage = page->next; otherpage;
                                    otherpage = otherpage->next)
          {
            if (has_parameters(otherpage->declarator))
            for (otherparam = otherpage->declarator->head->params.first;
                 otherparam;
                 otherparam = otherparam->next)
            {
                /* do these two look the same? */
                if (params_identical(param, otherparam))
                {
                  /* order is important for bit positions */
                  enum { NEITHER, US, THEM, BOTH } has_comm = NEITHER;
                  
                  /* work out who has the comment */
                  if (param->declarator->comment)     has_comm |= US;
                  if (otherparam->declarator->comment) has_comm |= THEM;

                  switch(has_comm)
                  {
                  case NEITHER:
                  case US:
                      otherparam->suppress = TRUE;
                      break;
                  case THEM:
                      param->suppress = TRUE;
                      break;
                  case BOTH:
                      if (!strcmp(param->declarator->comment,
                                  otherparam->declarator->comment))
                        otherparam->suppress = TRUE;
                      else
                      {
                        param->duplicate = TRUE;
                        otherparam->duplicate = TRUE;
                      }
                      break;
                  }
                }
            }
          }
      }
    }
    return any;
}

/* output a formatting string so that it works with filling on */
void output_format_string(fmt)
const char *fmt;
{
    while (*fmt)
    {
      output->character(*fmt);

      if (*fmt++ == '\n')
          output->break_line();     /* break the line */
    }
}

/* write the warning for the header */
void output_warning()
{
    output->comment();
    output->text("WARNING! THIS FILE WAS GENERATED AUTOMATICALLY BY ");
    output->text(progname);
    output->text("!\n");
    output->comment();
    output->text("DO NOT EDIT! CHANGES MADE TO THIS FILE WILL BE LOST!\n");
}

void output_includes()
{
    IncludeFile *incfile;
    
    for (incfile = first_include; incfile; incfile=incfile->next)
    {
      char *name = incfile->name;
      boolean surrounded = *name == '"' || *name == '<';
      
      output->text("#include ");
      if (!surrounded)  output->character('<');
      output->text(name);
      if (!surrounded)  output->character('>');
      output->text("\n");
      output->break_line();
      }
}

int exclude_section(section)
const char *section;
{
    ExcludeSection *exclude;

    for (exclude = first_excluded_section ; exclude ; exclude = exclude->next)
      if (!strcmp(section, exclude->name)) return 1;

    return 0;
}


/* Writes the entire contents of the manual page specified by basepage. */
void
output_manpage(firstpage, basepage, input_files, title, section)
    /* the first page in the list of all manual pages.  This is used to build
     * the SEE ALSO section of related pages when group_together is false.
     */
    ManualPage *firstpage;

    /* the base page from which the output manual page will be generated.  if
     * group_together indicates that the user wanted grouped pages, basepage
     * will always be the same as firstpage, and all the ManualPage's in the
     * list will be grouped together into the one output page.
     */
    ManualPage *basepage;

    int input_files;
    const char *title;
    const char *section;
{
    ManualPage *page;
    boolean need_returns;
    char *terseout, *terse = NULL;
    boolean exclude_description = exclude_section("DESCRIPTION");

    /* check if there's more than one page in the group */
    boolean grouped = group_together && firstpage->next;

    /* split up all the function comments for this page */
    for (page = basepage; page; page = page->next)
    {
      boolean explicit_description =
          split_function_comment(page->declarator->comment,
            page->declarator->name,
            group_together ? (char **)NULL : &terse,
            &page->description,&page->returns,&page->first_section);

      /* we may need to look harder if RETURNS wasn't easy to find in the
       * function comment.
       */
      if (page->returns == NULL)
      {
          /* if there was a retcomment supplied by the declarator, use it if
           * we couldn't split anything from the function comment.
           */
          if (page->declarator->retcomment)
          {
            page->returns = page->declarator->retcomment;

            /* page->returns now owns the string */
            page->declarator->retcomment = NULL;
          }
          else
            /* if there wasn't a RETURNS section, and the DESCRIPTION field
             * was not explicit, see if we can split one out of the
             * description field.
             */
            if (!explicit_description)
            {
                char *newdesc;
                if (split_returns_comment(page->description, &newdesc,
                                              &page->returns))
                {
                  free(page->description);
                  page->description = newdesc;
                }
            }
      }

      if (!group_together)    break;
    }

    /* work out what we'll actually print as a terse description */
    terseout = group_terse ? group_terse : (terse ? terse : "Not Described");

    output->header(basepage, input_files, grouped,
            title ? title : basepage->declarator->name, terseout, section);
    
    output->name(NULL);
    /* output the names of all the stuff documented on this page */
    for (page = basepage; page; page = page->next)
    {
      output->name(page->declarator->name);

      if (!group_together)    break;

      if (page->next)   output->text(",\n");
    }

    output->terse_sep();
    output->text(terseout);
    output->character('\n');
    
    if (!exclude_section("SYNOPSIS"))
    {
      output->section("SYNOPSIS");

      output->code_start();
    
      /* list the include files the user asked us to */
      output_includes();

      /* if it's a header file, say to #include it */
      if (header_file)
      {
          output->text("#include <");
          if (header_prefix)
          {
            output->text(header_prefix);
            output->character('/');
          }
          output->text(basefile);
          output->text(">\n");
      }

      /* can't just use .PP; that may reset our font */
      if (first_include || header_file)   output->blank_line();

      for (page = basepage; page; page = page->next)
      {
          output_format_string(decl_spec_prefix);

          /* make sure variables are prefixed extern */
          if (!(page->decl_spec->flags & DS_STATIC) &&
            !is_function_declarator(page->declarator) &&
            !strstr(page->decl_spec->text, "extern"))
            output->text("extern ");

          output_decl_spec(page->decl_spec);
          output_format_string(declarator_prefix);

          /* format it nicely if there's more than one parameter */
          output_declarator(page->declarator, 
            page->declarator->head->params.first !=
            page->declarator->head->params.last);

          output->text(";\n");
    
          if (!grouped) break;
          if (page->next)     output->blank_line();
      }

      output->code_end();
    }

    /* only output paramaters if there actually are some,
     * not including merely (void)
     */
    if (!exclude_section("PARAMETERS") &&
          ((grouped && mark_duplicate_parameters(basepage)) ||
           (!grouped && has_documented_parameters(basepage->declarator))))
    {
      output->section("PARAMETERS");

      for (page = basepage; page; page = page->next)
      {
          if (has_parameters(page->declarator))
            output_parameter_descriptions(&page->declarator->head->params,
                                        page->declarator->name);
          if (!grouped) break;      /* only do first page */
      }
    }

    if (!exclude_description)
      output->section("DESCRIPTION");

    if (grouped)
    {
      need_returns = FALSE;
      for (page = basepage; page; page = page->next)
      {
          if (needs_returns_section(page))      need_returns = TRUE;

          if (!exclude_description)
          {
            /* enum variables are documented in DESCRIPTION */
            if (auto_documented(page) &&
                            !is_function_declarator(page->declarator))
            {
                output->sub_section(page->declarator->name);
                output_identifier_description(page->description,
                  output_comment, page->decl_spec, page->declarator);
            }
            else if (page->description)
            {
                output->sub_section(page->declarator->name);
                output_comment(page->description);
            }
          }

          safe_free(page->description);
      }
    }
    else
    {
      need_returns = needs_returns_section(basepage);

      if (!exclude_description)
      {
          const char *descr = basepage->description
                              ? basepage->description : terseout;

          if (auto_documented(page) &&
                            !is_function_declarator(page->declarator))
            output_identifier_description(descr, output_comment,
                                  page->decl_spec, page->declarator);
          else
            output_comment(descr);
      }

      safe_free(basepage->description);
    }

    /* terse can now never be a static string */
    safe_free(terse);

    if (need_returns && !exclude_section("RETURNS"))
    {
      output->section("RETURNS");

      for (page = basepage; page; page = page->next)
      {
          if (needs_returns_section(page))
          {
            if (grouped) output->sub_section(page->declarator->name);

            output_identifier_description(page->returns, output->returns,
                                  page->decl_spec, page->declarator);
            safe_free(page->returns);
          }

          if (!grouped) break;
      }
    }

    /* output any other sections */
    for (page = basepage; page; page = page->next)
    {
      Section *section, *next;

      for (section = page->first_section; section; section = next)
      {
          next = section->next;

          if (!section->been_output && section->text &&
            strncmpi(section->text,"none",4) &&
             !exclude_section(section->name))
          {
            output->section(section->name);
            if (grouped) output->sub_section(page->declarator->name);
            output_comment(section->text);
            section->been_output = TRUE;
    
            if (grouped && page->next)
            {
                ManualPage *other_page = page->next;
    
                /* look through all the other pages for matching sections */
                for (; other_page; other_page = other_page->next)
                {
                  Section *other_section = other_page->first_section;
                  for (;other_section; other_section =
                                              other_section->next)
                  {
                      if (other_section->been_output ||
                        strcmp(other_section->name, section->name))
                        continue;
    
                      output->sub_section(other_page->declarator->name);
                      output_comment(other_section->text);
                      other_section->been_output = TRUE;
                  }
                }
            }
          }


          /* free this section */
          free(section->name);
          safe_free(section->text);
          free(section);
      }

      if (!grouped)     break;
    }

    /* only output SEE ALSO if not grouped */
    if (!group_together)
    {
      ManualPage *also;

      /* add the SEE ALSO section */
      /* look for any other functions to refer to */
      for (also = firstpage; also && also == basepage; also = also->next)
            ;
      
      if (also && !exclude_section("SEE ALSO")) /* did we find at least one? */
      {
          int isfirst = 1;

          output->section("SEE ALSO");
          
          for (also = firstpage; also; also = also->next)
          {
            if (also == basepage)   continue;
            
            if (!isfirst)
                output->text(",\n");
            else
                isfirst = 0;
                
            output->reference(also->declarator->name);
          }
      
          output->character('\n');
      }
    }

    if (!make_embeddable)
      output->file_end();
}


/* generate output filename based on a string */
char *page_file_name(based_on, object_type, extension)
    /* string to base the name on; this will be the name of an identifier or
     * the base of the input file name.
     */
    const char *based_on;
    enum Output_Object object_type; /* class of object documented */
    const char *extension;          /* file extension to use */
{
    char *filename;
    const char *subdir = output_object[object_type].subdir;

#ifndef FLEXFILENAMES
    char *basename;
    int chopoff = 14 - strlen(extension) - 1;

    basename = strduplicate(based_on);
    if (strlen(basename) > chopoff)
      basename[chopoff] = '\0';
#else
    const char *basename = based_on;
#endif

    filename = strduplicate(output_dir);

    if (subdir)
    {
      if (filename)     filename = strappend(filename, "/", NULLCP);
      filename = strappend(filename, subdir, NULLCP);
    }

    if (filename) filename = strappend(filename, "/", NULLCP);
    filename = strappend(filename, basename,".",extension, NULLCP);

#ifndef FLEXFILENAMES
    free(basename);
#endif
    return filename;
}

/* determine the output page type from a declaration */
enum Output_Object page_output_type(decl_spec, declarator)
const DeclSpec *decl_spec;
const Declarator *declarator;
{
    boolean is_static = decl_spec->flags & DS_STATIC;
    return is_function_declarator(declarator)
      ? (is_static ? OBJECT_STATIC_FUNCTION : OBJECT_FUNCTION)
      : (is_static ? OBJECT_STATIC_VARIABLE : OBJECT_VARIABLE);
}

/* determine the extension/section from an output type */
const char *page_manual_section(output_type)
enum Output_Object output_type;
{
    return  output_object[output_type].extension ?
            output_object[output_type].extension : manual_section;
}

/* remove an existing file, if it exists & we have write permission to it */
int remove_old_file(name)
const char *name;
{
#ifdef HAS_ACCESS
    /* check that we have write premission before blasting it */
    if (access(name,W_OK) == -1)
    {
      if (errno != ENOENT)
      {
          my_perror("can't access output file", name);
          return FALSE;
      }
     }
    else
#endif
    {
      /* if it exists, blast it */
      if (unlink(name) == -1 && errno != ENOENT)
      {
          my_perror("error unlinking old link file", name);
          return FALSE;
      }
    }
    return TRUE;
}

/* output all the manual pages in a list */
void output_manual_pages(first, input_files, link_type)
    ManualPage *first;
    int input_files;    /* number of different input files */
    enum LinkType link_type;  /* how grouped pages will be linked */
{
    ManualPage *page;
    int tostdout = output_dir && !strcmp(output_dir,"-");

    char *filename = NULL;

    /* output each page, in turn */
    for (page = first; page; page = page->next)
    {
      char *input_file_base = NULL;
      enum Output_Object output_type =
                  page_output_type(page->decl_spec, page->declarator);

      /* the manual name is used as the output file extension, and also in
       * the nroff output header.
       */
      const char *section = page_manual_section(output_type);

      /* work out the base name of the file this was generated from */
      if (page->sourcefile)
      {
          const char *base = strrchr(firstpage->sourcefile, '/');
          const char *last;
    
          /* use the file name as the manual page title */
          if (base == NULL)
            base = firstpage->sourcefile;
          else
            base++;
          last = strrchr(base, '.');
          if (last == NULL)
            last = base + strlen(base);
    
          input_file_base = alloc_string(base, last);
      }

      if (!tostdout)
      {
          safe_free(filename);      /* free previous, if any */
          filename = page_file_name(
            use_input_name && input_file_base
                        ? input_file_base : page->declarator->name,
            output_type, section);
          fprintf(stderr,"generating: %s\n",filename);

          /* a previous run may have left links, so nuke old file first */
          if (!remove_old_file(filename)) exit(1);

          if (freopen(filename, "w", stdout) == NULL)
          {
            my_perror("error opening output file", filename);
            free(filename);
            exit(1);
          }
      }

      /* do the page itself */
      output_manpage(first, page, input_files,
          group_together && input_file_base ? input_file_base
                                    : page->declarator->name,
          group_together ? manual_section : section);

      safe_free(input_file_base);

      /* don't continue if grouped, because all info went into this page */
      if (group_together)           break;

      if (tostdout &&   page->next) output->character('\f');
    }

    /* close the last output file if there was one */
    if (!tostdout && fclose(stdout) == EOF)
    {
      my_perror("error linking closing file", filename);
      exit(1);
    }

    /* if pages are grouped, just link the rest to the first */
    if (group_together && !tostdout && link_type != LINK_NONE)
    {
      for (page=use_input_name && first->sourcefile ? first : first->next;
                                        page; page = page->next)
      {
          enum Output_Object output_type =
                  page_output_type(page->decl_spec, page->declarator);
          const char *extension = page_manual_section(output_type);
          char *linkname = page_file_name(page->declarator->name,
                                          output_type, extension);
          int result = 0;

          /* we may have a function with the same name as the sourcefile */
          if (!strcmp(filename, linkname))
          {
            free(linkname);
            continue;
          }
                  
          fprintf(stderr,"%s: %s\n",
            link_type == LINK_REMOVE ? "removing" : "linking", linkname);

          /* always nuke old output file, since it may be linked to the one
           * we've just generated, so LINK_FILE may trash it.
           */
          if (!remove_old_file(linkname)) exit(1);

          switch(link_type)
          {
#ifdef HAS_LINK
          case LINK_HARD:
            result = link(filename, linkname);
            break;
#endif
#ifdef HAS_SYMLINK
          case LINK_SOFT:
            result = symlink(filename, linkname);
            break;
#endif
          case LINK_FILE:
            if (freopen(linkname, "w", stdout) == NULL)
            {
                result = -1;
                break;
            }
            output_warning();
            output->include(filename);
            if (fclose(stdout) == EOF)
                result = -1;
            break;
          case LINK_NONE:
          case LINK_REMOVE:
            break;
          }

          /* check it went OK */
          if (result == -1)
          {
            my_perror("error linking output file", linkname);
            exit(1);
          }
          free(linkname);
      }
    }

    safe_free(filename);
}

Generated by  Doxygen 1.6.0   Back to index