/* Output for c2man in raw text (ASCII) format
 * 1998 by B. Luevelsmeyer
 * This is a modified version of "nroff.c".
 */


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


static int indention = 0;
        /* The number of indents at line-start.
         * I print this number of tabs.
         * Used in ascii_text() and set in the tag_entry() functions.
         */

static void ascii_emphasized();

static void ascii_text(const char *text)
{
    if (indention>0)
      {
          const char *p;
          for (p = text; *p; p++)
            {
                putchar(*p);
                if (*p == '\n')
                  {
                      int i;
                      for (i = indention; i; i--)
                          put_string("\t");
                  }
            }
      }
    else
        put_string(text);
}

static void ascii_char(const int c)
{
    char b[2] = "x";
    b[0] = c;
    ascii_text(b);
}

static void ascii_comment(void)
{
    put_string("# ");
}



static void ascii_header(ManualPage * firstpage, int input_files, boolean grouped,
                        const char *name, const char * terse, const char *section)
{
#ifdef HAS_STRFTIME
    char month[20];
#else
    char *month;
    static char *month_list[] =
    {"January", "February", "March", "April", "May", "June",
     "July", "August", "September", "October", "November", "December"};
#endif
    Time_t raw_time;
    struct tm *filetime;

    indention = 0;

    if (make_embeddable) return;

    output_warning();

    /* if lots of files contributed, use the current time; otherwise use the
     * time of the source file they came from.
     */
    raw_time = (grouped && input_files > 1) ? time((Time_t *) NULL)
        : firstpage->sourcetime;

    filetime = localtime(&raw_time);

#ifdef HAS_STRFTIME
    /* generate the date format string */
    strftime(month, sizeof month, "%B", filetime);
#else
    month = month_list[filetime->tm_mon];
#endif

    ascii_text(name);

    printf("(%s) \t%d %s %d",
         section, filetime->tm_mday, month, filetime->tm_year + 1900);

    printf(", %s:", progname);
    if ((input_files <= 1 || !grouped) && firstpage->sourcefile)
      {
          const char *basename = strrchr(firstpage->sourcefile, '/');
          if (basename == NULL)
              basename = firstpage->sourcefile;
          else
              basename++;
          printf(" %s", basename);
      }
    if (manual_name)
        printf(" %s", manual_name);
    put_string("\n");

}

static void ascii_dash()
{
    ascii_text("-");
}

static void ascii_section(const char *name)
{
    indention=0;
    ascii_text("\n\n");
    ascii_emphasized(name);
    ascii_text("\n\n");
}

static void ascii_sub_section(const char *name)
{
    indention=0;
    ascii_text("\n");
    ascii_emphasized(name);
    indention++;
    ascii_text("\n\n");
}

static void ascii_break_line()
{
    ;
}

static void ascii_blank_line()
{
    ascii_text("\n");
}

static void ascii_code_start()
{
    ;
}

static void ascii_code_end()
{
    ;
}

static void ascii_code(const char *text)
{
    ascii_text("\n");
    ascii_text(text);
}

static void ascii_tag_list_start(void)
{
    indention++;
}

static void ascii_tag_list_end(void)
{
    indention--;
}

static void ascii_tag_entry_start(void)
{
    indention--;
    ascii_text("\n");
}

static void ascii_tag_entry_start_extra(void)
{
    indention--;
    ascii_text("\n");
}

static void ascii_tag_entry_end(void)
{
    indention++;
    ascii_text("\n");
}

static void ascii_tag_entry_end_extra(const char *text)
{
    ascii_text(" (");
    ascii_text(text);
    indention++;
    ascii_text(")\n");
}

static void ascii_table_start(const char *longestag)
{
    indention++;
    ascii_text("\n");
}

static void ascii_table_entry(const char *name, const char *description)
{
    ascii_code(name);
    if (description)
      {
          indention++;
          ascii_text("\n");
          output_comment(description);
          indention--;
      }
    else
        ascii_text("\n");
}

static void ascii_table_end(void)
{
    indention--;
    ascii_text("\n");
}

static void ascii_indent(void)
{
    ;
}

static void ascii_list_start(void)
{
    ascii_text("list");
}

static void ascii_list_entry(const char *name)
{
    ascii_code(name);
}

static void ascii_list_separator()
{
    ascii_text(",\n");
}

static void ascii_list_end()
{
    ascii_char('\n');
    ascii_table_end();
}

static void ascii_include(const char *filename)
{
    printf("(see also %s)\n", filename);
}


static void ascii_file_end(void)
{
    indention=0;
    puts("");       /* just in case there was no \n */
}


#define ascii_parse_option 0


static void ascii_print_options(void) { ; }


static void ascii_name(name)
const char *name;
{
    if (name) ascii_text(name);
    else      ascii_section("NAME");
}

static void ascii_terse_sep()
{
    ascii_char(' ');
    ascii_dash();
    ascii_char(' ');
}

static void ascii_emphasized(text)
const char *text;
{
    const char *p;
    if (make_embeddable) ascii_text(text);
    else
        for(p=text;*p;p++)
            if(isspace(*p)) ascii_char(*p);
            else
                { char r[4];
                  r[0] = r[2] = *p;
                  #ifdef DEBUG
                  r[1] = '_';
                  #else
                  r[1] = '\b';
                  #endif
                  r[3] = 0;
                  ascii_text(r);
                }
}

static void ascii_reference(text)
const char *text;
{
    ascii_text(text);
    ascii_char('(');
    ascii_text(manual_section);
    ascii_char(')');
}

static void ascii_description(text)
const char *text;
{
    enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE;
    boolean new_line = TRUE;
    boolean dot_command = FALSE;
    
    /* correct punctuation a bit as it goes out */
    for (;*text;text++)
    {
	int c = *text;

	if (dot_command)
	{
	    if (c == '\n')	dot_command = FALSE;
	}
	else if (new_line && c == '.')
	    dot_command = TRUE;
	else if (new_line && (c == '-' || c == '*' || is_numbered(text)))
	{
	    output->break_line();
	    state = CAPITALISE;
	}
	else if (c == '.')
	    state = PERIOD;
	else if (isspace(c) && state == PERIOD)
	    state = CAPITALISE;
	else if (isalnum(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 (!dot_command && state == TEXT)	output->character('.');
}

static void
ascii_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;

	/* explicitly reject dot commands */
	if (*comment && *comment != '.')
	{
	    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');

	/* dot commands go out unaltered */
	if (*comment == '.')
	{
	    for (;*comment && *comment != '\n'; comment++)
		output->character(*comment);
	    output->character('\n');
	}
	else
	{
	    /* 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 ascii_output =
{
    ascii_comment,
    ascii_header,
    ascii_dash,
    ascii_section,
    ascii_sub_section,
    ascii_break_line,
    ascii_blank_line,
    ascii_code_start,
    ascii_code_end,
    ascii_code,
    ascii_tag_list_start,
    ascii_tag_list_end,
    ascii_tag_entry_start,
    ascii_tag_entry_start_extra,
    ascii_tag_entry_end,
    ascii_tag_entry_end_extra,
    ascii_table_start,
    ascii_table_entry,
    ascii_table_end,
    ascii_indent,
    ascii_list_start,
    ascii_list_entry,
    ascii_list_separator,
    ascii_list_end,
    ascii_include,
    ascii_file_end,
    ascii_text,
    ascii_char,
    ascii_parse_option,
    ascii_print_options,
    ascii_name,
    ascii_terse_sep,
    ascii_reference,
    ascii_emphasized,
    ascii_description,
    ascii_returns
};
