/*
** $PROJECT:
**
** $VER: autodoc.c 2.2 (25.01.95)
**
** by
**
** Stefan Ruppert , Windthorststra_e 5 , 65439 Flvrsheim , GERMANY
**
** (C) Copyright 1995
** All Rights Reserved !
**
** $HISTORY:
**
** 25.01.95 : 002.002 : changed to patchlevel 33
** 22.01.95 : 000.001 : initial
*/

#include "c2man.h"
#include "manpage.h"
#include "output.h"
#include <ctype.h>

#ifdef DEBUG
#define D(x)  x
#else
#define D(x)
#endif

#define MAX_TAG      10

static const int linelength   = 79;
static const int tablength    =  4;
static const int indentlength =  4;

static int indent      = 4;
static int list_indent = 0;
static int column      = 0;
static int newline     = FALSE;
static int breakline   = FALSE;
static int see_also    = FALSE;
static int fileend     = FALSE;
static int acttable    = -1;
static int tablemaxtag[MAX_TAG];

void autodoc_format(text)
const char *text;
{
    if(see_also)
    {
       if(column + ((text) ? strlen(text) + 2 : 1) > linelength)
       {
           putchar('\n');
           newline = TRUE;
       }
    }

    if(newline)
    {
       int i;
       column = i = indent + list_indent;
       for(; i ; i--)
          putchar(' ');
       newline = FALSE;
    }
}

void autodoc_text(text)
const char *text;
{
    int br = 1;
    autodoc_format(text);

    if(!see_also || (br = strcmp(text,",\n")))
    {
        if(see_also < 2)
        {
           put_string(text);
           column += strlen(text);
        }
    } else if(!br)
    {
        column += 2;
        put_string(", ");
    }
}

void autodoc_char(c)
const int c;
{
    if(c != '\f')
    {
       autodoc_format(NULL);

       if(c == '\t')
       {
          int i = tablength - (column % tablength);
          column += i;
          for(; i ; i--)
             putchar(' ');
       } else
       {
          if(see_also)
          {
             if(c == '(')
                 see_also++;
             else if(c == ')')
                 see_also--;
          }

          putchar(c);
          column++;
       }

       if((newline = (c == '\n')))
           column = 0;
   }
}

void autodoc_comment() { }

void autodoc_header(firstpage, input_files, grouped, name, terse, section)
ManualPage *firstpage;
int input_files;
boolean grouped;
const char *name;
const char *terse;
const char *section;
{
    const char *basename = strrchr(firstpage->sourcefile, '/');
    int len;
    int spc;

    fileend = FALSE;

    if(basename && *basename == '/')
        basename++;

    len =  ((basename) ? strlen(basename) + 1 : 0) + strlen(name);
    spc  = linelength - 2 * len;

    see_also = FALSE;

    if(basename)
    {
       autodoc_text(basename);
       autodoc_char('/');
    }
    autodoc_text(name);

    if(spc > 0)
    {
      while(spc)
      {
         autodoc_char(' ');
         spc--;
      }
       if(basename)
       {
          autodoc_text(basename);
          autodoc_char('/');
       }
       autodoc_text(name);
    } else
    {
       const char *ptr = name;
       len = linelength - 1 - len;

       while(len)
       {
          if(basename && *basename)
          {
             autodoc_char(*basename);
             basename++;
          } else
          {
             if(ptr == name && basename)
                autodoc_char('/');
             else
             {
                autodoc_char(*ptr);
                ptr++;
             }
          }
          len--;
       }
    }

    put_string("\n");
}

void autodoc_dash()       { put_string("-"); }

void autodoc_section(name)
const char *name;
{
    D((fprintf(stderr,"section : %s\n",name)));
    newline = FALSE;
    see_also = FALSE;
    put_string("\n");
    if(!strcmp(name,"DESCRIPTION"))
       name = "FUNCTION";
    else if(!strcmp(name,"PARAMETERS"))
       name = "INPUTS";
    else if(!strcmp(name,"RETURNS"))
       name = "RESULT";
    else if(!strcmp(name,"SEE ALSO"))
       see_also = TRUE;

    put_string("    ");
    autodoc_text(name);
    indent = 8;
    list_indent = 0;
    autodoc_char('\n');
}

void autodoc_sub_section(name)
const char *name;
{
    autodoc_text(name);
    indent = 12;
}

void autodoc_break_line()
{
   breakline = TRUE;
}

void autodoc_blank_line()
{
    autodoc_char('\n');
}

void autodoc_code_start() {  }
void autodoc_code_end()   {  }

void autodoc_code(text)
const char *text;
{
    autodoc_text(text);
}

void autodoc_tag_entry_start()
{
    if(list_indent > 0)
    {
        autodoc_char('\n');
        list_indent -= indentlength;
    }
}
void autodoc_tag_entry_start_extra()
{
    if(list_indent > 0)
    {
        autodoc_char('\n');
        list_indent -= indentlength;
    }
}
void autodoc_tag_entry_end()
{
   list_indent += indentlength;
   autodoc_char('\n');
}
void autodoc_tag_entry_end_extra(text)
const char *text;
{
    put_string("\" \"\t(");
    autodoc_text(text);
    put_string(")\"\n");
    list_indent += indentlength;
}
        
void autodoc_table_start(longestag)
const char *longestag;
{
   if(acttable < MAX_TAG - 1)
   {
      acttable++;
      tablemaxtag[acttable] = strlen(longestag);
   }

   indent += indentlength;
   newline = TRUE;
}

void autodoc_table_entry(name, description)
const char *name;
const char *description;
{
    int i = tablemaxtag[acttable] - strlen(name) + 1;

    autodoc_code(name);
    while(i > 0)
    {
       putchar(' ');
       i--;
    }
    putchar('-');
    putchar(' ');

    if (description)
        output_comment(description);
    else
        autodoc_char('\n');
}

void autodoc_table_end()
{
    if(acttable > -1)
      acttable--;

    autodoc_char('\n');
    indent -= indentlength;
    if(list_indent > 0)
       list_indent -= indentlength;
}

void autodoc_indent()
{
    int i;
    for(i = indent + list_indent; i ; i--)
       autodoc_char(' ');
}

void autodoc_list_start()
{
   indent += indentlength;
   newline = TRUE;
}

void autodoc_list_entry(name)
const char *name;
{
    autodoc_code(name);
}

void autodoc_list_separator() { put_string(" ,"); }
void autodoc_list_end()   { autodoc_char('\n'); autodoc_table_end(); }

void autodoc_include(filename)
const char *filename;
{

}

void autodoc_terse_sep()
{
    autodoc_char(' ');
    autodoc_dash();
    autodoc_char(' ');
}

void autodoc_name(name)
const char *name;
{
   if(name)
      autodoc_text(name);
   else
      autodoc_section("NAME");
}

void autodoc_file_end()
{
   if(!fileend)
      putchar('\f');
   fileend = TRUE;
   newline = FALSE;
}

/* ideally, this should be made aware of embedded autodoc commands */
void autodoc_description(text)
const char *text;
{
    enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE;
    boolean new_line = TRUE;
    
    /* correct punctuation a bit as it goes out */
    for (;*text;text++)
    {
	int c = *text;

	if (new_line && (c == '-' || c == '*'))
	{
	    output->break_line();
	    state = CAPITALISE;
	}
	else if (c == '.')
	    state = PERIOD;
	else if (isspace(c) && state == PERIOD)
	    state = CAPITALISE;
	else if (isalnum(c) || ispunct(c))
	{   
	    if (islower(c) && state == CAPITALISE)	c = toupper(c);
	    state = TEXT;
	}
           
	output->character(c);
	new_line = c == '\n';
    }

    /* do a full stop if there wasn't one */
    if (state == TEXT)	output->character('.');
}

/* ideally, this should be made aware of embedded autodoc commands */
void
autodoc_returns(comment)
const char *comment;
{
    enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE;
    char lastchar = '\n';
    boolean tag_list_started = FALSE;

    /* for each line... */
    while (*comment)
    {
	boolean tagged = FALSE;

	{
	    const char *c = comment;

	    /* search along until the end of a word */
	    while (*c && *c != ':' && !isspace(*c))
		c++;

	    /* skip all spaces or tabs after the first word */
	    while (*c && *c != '\n')
	    {
		if (*c == '\t' || *c == ':')
		{
		    tagged = TRUE;
		    break;
		}
		else if (!isspace(*c))
		    break;

		c++;
	    }
	}

	/* is it tagged?; explicitly reject dot commands */
	if (tagged)
	{
	    /* output lingering newline if necessary */
	    if (lastchar != '\n')
	    {
		if (state == TEXT && !ispunct(lastchar))	output->character('.');
		output->character(lastchar = '\n');
	    }

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

	    /* output the taggy bit */
	    output->tag_entry_start();
	    while (*comment && *comment != ':' && !isspace(*comment))
		output->character(*comment++);
	    output->tag_entry_end();

	    /* skip any extra tabs or spaces */
	    while (*comment == ':' || (isspace(*comment) && *comment != '\n'))
		comment++;

	    state = CAPITALISE;
	}

	/* terminate the previous line if necessary */
	if (lastchar != '\n')	output->character(lastchar = '\n');

	/* correct punctuation a bit as the line goes out */
	for (;*comment && *comment != '\n'; comment++)
	{
	    char c = *comment;

	    if (c == '.')
		state = PERIOD;
	    else if (isspace(c) && state == PERIOD)
		state = CAPITALISE;
	    else if (isalnum(c))
	    {   
		if (islower(c) && state == CAPITALISE && fixup_comments)
		    c = toupper(c);
		state = TEXT;
	    }

	    output->character(lastchar = c);
	}

	/* if it ended in punctuation, just output the nl straight away. */
	if (ispunct(lastchar))
	{
	    if (lastchar == '.')	state = CAPITALISE;
	    output->character(lastchar = '\n');
	}

	if (*comment)	comment++;
    }

    /* output lingering newline if necessary */
    if (lastchar != '\n')
    {
	if (state == TEXT && !ispunct(lastchar) && fixup_comments)
	    output->character('.');
	output->character('\n');
    }

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


struct Output autodoc_output =
{
    autodoc_comment,
    autodoc_header,
    autodoc_dash,
    autodoc_section,
    autodoc_sub_section,
    autodoc_break_line,
    autodoc_blank_line,
    autodoc_code_start,
    autodoc_code_end,
    autodoc_code,
    dummy,              /* autodoc_tag_list_start */
    dummy,              /* autodoc_tag_list_end */
    autodoc_tag_entry_start,
    autodoc_tag_entry_start_extra,
    autodoc_tag_entry_end,
    autodoc_tag_entry_end_extra,
    autodoc_table_start,
    autodoc_table_entry,
    autodoc_table_end,
    autodoc_indent,
    autodoc_list_start,
    autodoc_code,         /* autodoc_list_entry */
    autodoc_list_separator,
    autodoc_list_end,
    autodoc_include,
    autodoc_file_end,      /* autodoc_file_end */
    autodoc_text,
    autodoc_char,
    NULL,                  /* autodoc_parse_option */
    dummy,                 /* autodoc_print_options */
    autodoc_name,
    autodoc_terse_sep,
    autodoc_text,          /* autodoc_reference */
    autodoc_text,          /* autodoc_emphasized */
    autodoc_description,
    autodoc_returns
};

