diff options
author | behdad <behdad> | 2004-05-03 05:17:48 +0000 |
---|---|---|
committer | behdad <behdad> | 2004-05-03 05:17:48 +0000 |
commit | 577ed4095383ef5284225d45709e6b5f0598a064 (patch) | |
tree | 6c7d0ce55124a688b4d7050e684d9d7a1e3aa71d /nroff.c | |
download | c2man-master.tar.gz |
Diffstat (limited to 'nroff.c')
-rw-r--r-- | nroff.c | 424 |
1 files changed, 424 insertions, 0 deletions
@@ -0,0 +1,424 @@ +/* $Id: nroff.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * functions for nroff style output. + */ +#include "c2man.h" +#include "manpage.h" +#include "output.h" +#include "semantic.h" +#include <ctype.h> + +void nroff_text(text) +const char *text; +{ + put_string(text); +} + +void nroff_char(c) +const int c; +{ + putchar(c); +} + +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; + + /* 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('.'); +} + +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 +}; |