/* $Id: latex.c,v 2.0.1.16 2000/02/25 04:15:38 greyham Exp greyham $
 * functions for LaTeX style output.
 */
#include "c2man.h"
#include "manpage.h"
#include "output.h"
#include <ctype.h>

static boolean verbatim = FALSE;	/* skip quoting in verbatim section */

void latex_char(c)
const int c;
{
    int i;

    switch (c)
    {
    case '$':
    case '#':
    case '&':
    case '^':
    case '_':
	if (!verbatim)
	    putchar('\\');
	putchar(c);
	break;
    case '>':
    case '<':
	if (!verbatim)
	    putchar('$');
	putchar(c);
	if (!verbatim)
	    putchar('$');
	break;
    case '\t':
	for (i = 0; i < NUM_TAB_SPACES; i++)
	    putchar(' ');
	break;
    case '\\':
	if (!verbatim) {
            put_string("$\\backslash$");
	} else
	   putchar(c);
        break;

    default:
	putchar(c);
	break;
    }
}

void latex_text(text)
const char *text;
{
    while(*text)
	latex_char(*text++);
}

void latex_comment() { put_string("% "); }

void latex_header(firstpage, input_files, grouped, name, terse, section)
ManualPage *firstpage;
int input_files;
boolean grouped;
const char *name;
const char *terse;
const char *section;
{
    if (make_embeddable) return;

    put_string("\\documentstyle{article}\n");
    output_warning();
    put_string("\\begin{document}\n");
    put_string("\\setlength{\\parindent}{0pt}\n");
}

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

void latex_section(name)
const char *name;
{
    put_string("\\section*{");
    latex_text(name);
    put_string("}\n");
}

void latex_sub_section(name)
const char *name;
{
    put_string("\\subsection*{");
    latex_text(name);
    put_string("}\n");
}

void latex_break_line() { /* put_string("\\\n"); */}
void latex_blank_line() { put_string("\n"); }

void latex_code_start() { put_string("\\begin{verbatim}\n"); verbatim = TRUE; }
void latex_code_end()	{ put_string("\\end{verbatim}\n"); verbatim = FALSE; }

void latex_code(text)
const char *text;
{
    put_string("\\verb`");
    put_string(text);
    put_string("`");
}

void latex_tag_list_start()	{ put_string("\\begin{description}\n"); }
void latex_tag_entry_start()	{ put_string("\\item["); }
void latex_tag_entry_end()	{ put_string("]\\hfill\\newline\n"); }
void latex_tag_entry_end_extra(text)
const char *text;
{
    put_string("(");
    latex_text(text);
    put_string(")");
	latex_tag_entry_end();
}
void latex_tag_list_end()	{ put_string("\\end{description}\n"); }
	
void latex_table_start(longestag)
const char *longestag;
{ put_string("\\begin{description}\n"); }

void latex_table_entry(name, description)
const char *name;
const char *description;
{
    put_string("\\item[");
    latex_text(name);
    put_string("]\n");
    if (description)
	output_comment(description);
    else
	latex_char('\n');
}

void latex_table_end()	{ put_string("\\end{description}\n"); }

void latex_list_entry(text)
const char *text;
{
    latex_text(text);
}
void latex_list_separator() { put_string(",\n"); }
void latex_list_end()	{ latex_char('\n'); }

void latex_include(filename)
const char *filename;
{
	put_string("\\include{");
	latex_text(filename);
	put_string("}\n");
}

void latex_file_end() { put_string("\\end{document}\n"); }

void latex_name(name)
const char *name;
{
    if (name) latex_text(name);
    else      latex_section("NAME");
}

void latex_terse_sep()
{
    latex_char(' ');
    latex_dash();
    latex_char(' ');
}

void latex_reference(text)
const char *text;
{
    latex_text(text);
    latex_char('(');
    latex_text(manual_section);
    latex_char(')');
}

/* ideally, this should be made aware of embedded latex commands */
void latex_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 && verbatim && c != '\t') {
	latex_code_end(); /* \end{verbatim}; verbatim=FALSE; */
      }
      
      if (new_line && c == '\t') {
	if (!verbatim) {
	  latex_code_start(); /* \begin{verbatim}; verbatim=TRUE; */
	}
      } else if (new_line && (c == '-' || c == '*')) {
	/* latex output-> break_line() does nothing
	 * output->break_line();
	 */
	put_string("\\\\\n");
	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 && !verbatim) c = toupper(c);
	state = TEXT;
      }
      
      output->character(c);
      new_line = c == '\n';
    }

    if ( verbatim ) { /* end verbatim section at end of comments */
      latex_code_end();	
    } else /* no extra point after verbatim section */
      /* do a full stop if there wasn't one */
      if (state == TEXT)	output->character('.');
}

/* ideally, this should be made aware of embedded latex commands */
void
latex_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 latex_output =
{
    latex_comment,
    latex_header,
    latex_dash,
    latex_section,
    latex_sub_section,
    latex_break_line,
    latex_blank_line,
    latex_code_start,
    latex_code_end,
    latex_code,
    latex_tag_list_start,
    latex_tag_list_end,
    latex_tag_entry_start,
    latex_tag_entry_start,	/* entry_start_extra */
    latex_tag_entry_end,
    latex_tag_entry_end_extra,
    latex_table_start,
    latex_table_entry,
    latex_table_end,
    dummy,		/* latex_indent */
    dummy,		/* latex_list_start */
    latex_list_entry,
    latex_list_separator,
    latex_list_end,
    latex_include,
    latex_file_end,
    latex_text,
    latex_char,
    NULL,		/* latex_parse_option */
    dummy,		/* latex_print_option */
    latex_name,
    latex_terse_sep,
    latex_reference,
    latex_text,
    latex_description,
    latex_returns
};


syntax highlighted by Code2HTML, v. 0.9.1