summaryrefslogtreecommitdiff
path: root/nroff.c
diff options
context:
space:
mode:
Diffstat (limited to 'nroff.c')
-rw-r--r--nroff.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/nroff.c b/nroff.c
new file mode 100644
index 0000000..174d8b4
--- /dev/null
+++ b/nroff.c
@@ -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
+};