/* $Id: nroff.c,v 2.0.1.24 2000/02/25 04:15:37 greyham Exp greyham $
* functions for nroff style output.
*/
#include "c2man.h"
#include "manpage.h"
#include "output.h"
#include "semantic.h"
#include <ctype.h>
void nroff_char(c)
const int c;
{
switch (c)
{
case '\\':
put_string("\\\\");
break;
default:
putchar(c);
break;
}
}
void nroff_text(text)
const char *text;
{
while(*text)
nroff_char(*text++);
}
void nroff_comment() { put_string(".\\\" "); }
void nroff_header(firstpage, input_files, grouped, name, terse, section)
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;
if (make_embeddable) return;
output_warning();
put_string(".TH \"");
/* 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
nroff_text(name);
printf("\" %s \"%d %s %d\"",
section,filetime->tm_mday,month,filetime->tm_year+1900);
/* I have conflicting info about how the .TH macro works.... */
#ifdef HENRYS_TH /* As per Henry Spencer's "How to write a manual page" */
if (manual_name) printf(" \"%s\"", manual_name);
put_string("\n.BY");
#endif
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);
}
#ifndef HENRYS_TH
if (manual_name) printf("\" \"%s", manual_name);
#endif
put_string("\"\n");
#ifdef NeXT
/* define our own .SS on packages (such as NeXT's) where it doesn't move
* left a little. Sorry, awf doesn't support .SS.
*/
put_string(".de SS\n.}X .25i \"\" \"\"\n.nr )E 2\n");
put_string("\\&\\\\$1\n.br\n..\n");
#endif
}
void nroff_dash() { put_string("\\-"); }
void nroff_section(name)
const char *name;
{
put_string(".SH \"");
nroff_text(name);
put_string("\"\n");
}
void nroff_sub_section(name)
const char *name;
{
put_string(".SS \"");
nroff_text(name);
put_string("\"\n");
}
void nroff_break_line() { put_string(".br\n"); }
void nroff_blank_line() { put_string(".sp\n"); }
void nroff_code_start() { put_string(".ft B\n"); }
void nroff_code_end() { put_string(".ft R\n"); }
void nroff_code(text)
const char *text;
{
put_string("\\fB");
nroff_text(text);
put_string("\\fR");
}
void nroff_tag_entry_start() { put_string(".TP\n.B \""); }
void nroff_tag_entry_start_extra() { put_string(".TP\n.BR \""); }
void nroff_tag_entry_end() { put_string("\"\n"); }
void nroff_tag_entry_end_extra(text)
const char *text;
{
put_string("\" \"\t(");
nroff_text(text);
put_string(")\"\n");
}
void nroff_table_start(longestag)
const char *longestag;
{
void nroff_list_start();
nroff_list_start();
/* We measure the length of the longest tag in the table by changing to the
* code font, taking it's width with \w'string' and adding a little for
* the space between the tag and description. This gets stored in the TL
* number register, where the nroff_table_entry can find it.
* This isn't foolproof, because a shorter tag may be longer if it contains
* wider characters, but the extra space gives a little head room anyway.
*/
nroff_code_start();
printf(".nr TL \\w'%s'u+0.2i\n", longestag);
nroff_code_end();
}
void nroff_table_entry(name, description)
const char *name;
const char *description;
{
put_string(".TP \\n(TLu\n");
nroff_code(name);
nroff_char('\n');
if (description)
output_comment(description);
else
nroff_char('\n');
}
void nroff_table_end() { put_string(".RE\n.PD\n"); }
void nroff_indent() { put_string(".IP\n"); }
void nroff_list_start() { put_string(".RS 0.75in\n.PD 0\n"); }
void nroff_list_entry(name)
const char *name;
{
nroff_code(name);
}
void nroff_list_separator() { put_string(",\n"); }
void nroff_list_end() { nroff_char('\n'); nroff_table_end(); }
void nroff_include(filename)
const char *filename;
{
printf(".so %s\n", filename);
}
void nroff_name(name)
const char *name;
{
if (name) nroff_text(name);
else nroff_section("NAME");
}
void nroff_terse_sep()
{
nroff_char(' ');
nroff_dash();
nroff_char(' ');
}
void nroff_emphasized(text)
const char *text;
{
put_string("\\fI");
nroff_text(text);
put_string("\\fR");
}
void nroff_reference(text)
const char *text;
{
nroff_text(text);
nroff_char('(');
nroff_text(manual_section);
nroff_char(')');
}
void nroff_description(text)
const char *text;
{
enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE;
boolean new_line = TRUE;
boolean dot_command = FALSE;
boolean verbatim = FALSE;
/* correct punctuation a bit as it goes out */
for (;*text;text++) {
int c = *text;
if (new_line && verbatim && c != '\t') {
verbatim = FALSE;
output->text(".fi\n");
}
if (dot_command) {
if (c == '\n') dot_command = FALSE;
}
else if (new_line && c == '\t') {
if (!verbatim) {
output->text(".nf\n");
verbatim = TRUE;
}
}
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) || ispunct(c)) {
if (islower(c) && state == CAPITALISE && !verbatim) c = toupper(c);
state = TEXT;
}
if (new_line && c == '\'')
output->character('\\');
output->character(c);
new_line = c == '\n';
}
if ( verbatim ) { /* end verbatim section at end of comments */
output->text("\n.fi\n");
} else /* no extra point after verbatim section */
/* do a full stop if there wasn't one */
if (!dot_command && state == TEXT) output->character('.');
}
void
nroff_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 nroff_output =
{
nroff_comment,
nroff_header,
nroff_dash,
nroff_section,
nroff_sub_section,
nroff_break_line,
nroff_blank_line,
nroff_code_start,
nroff_code_end,
nroff_code,
dummy, /* nroff_tag_list_start */
dummy, /* nroff_tag_list_end */
nroff_tag_entry_start,
nroff_tag_entry_start_extra,
nroff_tag_entry_end,
nroff_tag_entry_end_extra,
nroff_table_start,
nroff_table_entry,
nroff_table_end,
nroff_indent,
nroff_list_start,
nroff_code, /* nroff_list_entry */
nroff_list_separator,
nroff_list_end,
nroff_include,
dummy, /* nroff_file_end */
nroff_text,
nroff_char,
NULL, /* nroff_parse_option */
dummy, /* nroff_print_options */
nroff_name,
nroff_terse_sep,
nroff_reference,
nroff_emphasized,
nroff_description,
nroff_returns
};
syntax highlighted by Code2HTML, v. 0.9.1